简历推荐功能开发
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
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:
206
src/pages/Management/List/ResumeRecommend/InterviewInvite.tsx
Normal file
206
src/pages/Management/List/ResumeRecommend/InterviewInvite.tsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import { Modal, Card, Form, Select, Input, DatePicker, Button, message } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { addCmsInterview, getCmsInterviewList } from '@/services/Management/list';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
|
||||
export type InterviewInviteProps = {
|
||||
onClose: (needRefresh?: boolean) => void;
|
||||
open: boolean;
|
||||
userId?: number;
|
||||
userName?: string;
|
||||
jobId?: string;
|
||||
companyId?: number;
|
||||
jobName?: string;
|
||||
};
|
||||
|
||||
const InterviewInvite: React.FC<InterviewInviteProps> = (props) => {
|
||||
const { open, userId, jobId, companyId, jobName } = props;
|
||||
|
||||
const [interviewForm] = Form.useForm();
|
||||
const [interviewMethod, setInterviewMethod] = useState<string>('online');
|
||||
const [submitting, setSubmitting] = useState<boolean>(false);
|
||||
const [alreadyInvited, setAlreadyInvited] = useState<boolean>(false);
|
||||
const [checkingInvitation, setCheckingInvitation] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && userId && jobId) {
|
||||
checkExistingInvitation();
|
||||
interviewForm.resetFields();
|
||||
setInterviewMethod('online');
|
||||
}
|
||||
}, [open, userId, jobId]);
|
||||
|
||||
// 检查是否已存在有效邀约
|
||||
const checkExistingInvitation = async () => {
|
||||
if (!jobId || !userId) return;
|
||||
setCheckingInvitation(true);
|
||||
try {
|
||||
const res = await getCmsInterviewList({
|
||||
jobId: Number(jobId),
|
||||
userId: userId,
|
||||
});
|
||||
if (res?.code === 200 && res?.rows?.length > 0) {
|
||||
const hasActive = res.rows.some(
|
||||
(item: API.Management.InterviewInvitation) => item.status !== 'rejected'
|
||||
);
|
||||
setAlreadyInvited(hasActive);
|
||||
} else {
|
||||
setAlreadyInvited(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查邀约状态失败:', error);
|
||||
} finally {
|
||||
setCheckingInvitation(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 发送面试邀约
|
||||
const handleInterviewSubmit = async () => {
|
||||
try {
|
||||
const values = await interviewForm.validateFields();
|
||||
setSubmitting(true);
|
||||
|
||||
const payload: API.Management.InterviewInvitation = {
|
||||
companyId: companyId,
|
||||
jobId: Number(jobId),
|
||||
userId: userId,
|
||||
jobName: jobName,
|
||||
interviewTime: values.interviewTime
|
||||
? dayjs(values.interviewTime).format('YYYY-MM-DD HH:mm:ss')
|
||||
: undefined,
|
||||
interviewMethod: values.interviewMethod,
|
||||
meetingLink: values.meetingLink,
|
||||
meetingPassword: values.meetingPassword,
|
||||
interviewLocation: values.interviewLocation,
|
||||
contactPhone: values.contactPhone,
|
||||
interviewerName: values.interviewerName,
|
||||
status: 'pending',
|
||||
remark: values.remark,
|
||||
};
|
||||
|
||||
const response = await addCmsInterview(payload);
|
||||
if (response.code === 200) {
|
||||
message.success('面试邀约已发送');
|
||||
setAlreadyInvited(true);
|
||||
interviewForm.resetFields();
|
||||
setInterviewMethod('online');
|
||||
} else {
|
||||
message.error(response.msg || '发送失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
const errMsg = error?.response?.data?.msg || error?.message || '发送面试邀约失败';
|
||||
message.error(errMsg);
|
||||
console.error('发送面试邀约失败:', error);
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onCancel={() => props.onClose()}
|
||||
title="邀请面试"
|
||||
width={700}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={() => props.onClose()}>
|
||||
取消
|
||||
</Button>,
|
||||
]}
|
||||
destroyOnClose
|
||||
>
|
||||
<Card title="面试邀约" bordered>
|
||||
<Form
|
||||
form={interviewForm}
|
||||
layout="vertical"
|
||||
initialValues={{ interviewMethod: 'online', meetingLink: 'https://mc.repohub.cn/pro.html' }}
|
||||
>
|
||||
<Form.Item
|
||||
name="interviewMethod"
|
||||
label="面试方式"
|
||||
rules={[{ required: true, message: '请选择面试方式' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择面试方式"
|
||||
onChange={(value) => setInterviewMethod(value)}
|
||||
>
|
||||
<Option value="online">线上</Option>
|
||||
<Option value="offline">线下</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="interviewTime"
|
||||
label="面试时间"
|
||||
rules={[{ required: true, message: '请选择面试时间' }]}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请选择面试时间"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{interviewMethod === 'online' && (
|
||||
<Form.Item
|
||||
name="meetingLink"
|
||||
label="在线会议链接"
|
||||
>
|
||||
<Input placeholder="请输入在线会议链接" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
{interviewMethod === 'offline' && (
|
||||
<Form.Item
|
||||
name="interviewLocation"
|
||||
label="面试地点"
|
||||
rules={[{ required: true, message: '请输入面试地点' }]}
|
||||
>
|
||||
<Input placeholder="请输入面试地点" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="remark"
|
||||
label="备注"
|
||||
>
|
||||
<TextArea rows={3} placeholder="选填,如有其他注意事项可在此说明" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="interviewerName"
|
||||
label="面试官姓名"
|
||||
rules={[{ required: true, message: '请输入面试官姓名' }]}
|
||||
>
|
||||
<Input placeholder="请输入面试官姓名" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="contactPhone"
|
||||
label="面试官联系方式"
|
||||
rules={[{ required: true, message: '请输入面试官联系方式' }]}
|
||||
>
|
||||
<Input placeholder="请输入手机号或微信等联系方式" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={submitting || checkingInvitation}
|
||||
disabled={alreadyInvited}
|
||||
onClick={handleInterviewSubmit}
|
||||
block
|
||||
>
|
||||
{alreadyInvited ? '已邀约' : '发送面试邀约'}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default InterviewInvite;
|
||||
211
src/pages/Management/List/ResumeRecommend/ResumeDetail.tsx
Normal file
211
src/pages/Management/List/ResumeRecommend/ResumeDetail.tsx
Normal file
@@ -0,0 +1,211 @@
|
||||
import { Modal, Descriptions, List, Card, Tag, Empty, Spin } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { addCmsUserWorkExperiencesList, getAppSkillList } from '@/services/Management/list';
|
||||
|
||||
export type ResumeDetailProps = {
|
||||
onClose: (flag?: boolean) => void;
|
||||
open: boolean;
|
||||
values?: API.ManagementList.RecommendUser;
|
||||
jobId?: string;
|
||||
};
|
||||
|
||||
const ResumeDetail: React.FC<ResumeDetailProps> = (props) => {
|
||||
const { values, jobId } = props;
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [experienceList, setExperienceList] = useState<API.ManagementList.RecommendExperience[]>([]);
|
||||
const [skillList, setSkillList] = useState<API.ManagementList.RecommendSkill[]>([]);
|
||||
|
||||
// 性别映射
|
||||
const sexMap: Record<string, string> = {
|
||||
'0': '男',
|
||||
'1': '女',
|
||||
};
|
||||
|
||||
// 学历映射(常用值)
|
||||
const educationMap: Record<string, string> = {
|
||||
'1': '初中及以下',
|
||||
'2': '高中',
|
||||
'3': '中专',
|
||||
'4': '大专',
|
||||
'5': '本科',
|
||||
'6': '硕士',
|
||||
'7': '博士',
|
||||
};
|
||||
|
||||
// 政治面貌映射
|
||||
const politicalMap: Record<string, string> = {
|
||||
'1': '中共党员',
|
||||
'2': '中共预备党员',
|
||||
'3': '共青团员',
|
||||
'4': '群众',
|
||||
'5': '民主党派',
|
||||
};
|
||||
|
||||
// 弹窗打开时获取工作经历和技能
|
||||
useEffect(() => {
|
||||
if (props.open && values?.userId) {
|
||||
fetchWorkExperiences();
|
||||
fetchSkills();
|
||||
} else {
|
||||
setExperienceList([]);
|
||||
setSkillList([]);
|
||||
}
|
||||
}, [props.open, values?.userId]);
|
||||
|
||||
const fetchWorkExperiences = async () => {
|
||||
if (!values?.userId) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const params: any = { userId: values.userId };
|
||||
if (jobId) {
|
||||
params.jobId = Number(jobId);
|
||||
}
|
||||
const response = await addCmsUserWorkExperiencesList(params);
|
||||
if (response.code === 200) {
|
||||
setExperienceList(response.rows || []);
|
||||
} else {
|
||||
setExperienceList(values.experiencesList || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取工作经历失败:', error);
|
||||
setExperienceList(values.experiencesList || []);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchSkills = async () => {
|
||||
if (!values?.userId) return;
|
||||
|
||||
try {
|
||||
const response = await getAppSkillList({ userId: values.userId });
|
||||
if (response.code === 200) {
|
||||
setSkillList(response.rows || []);
|
||||
} else {
|
||||
setSkillList(values.appSkillsList || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取技能信息失败:', error);
|
||||
setSkillList(values.appSkillsList || []);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={props.open}
|
||||
onCancel={() => props.onClose()}
|
||||
title="简历详情"
|
||||
width={900}
|
||||
footer={null}
|
||||
destroyOnClose
|
||||
>
|
||||
{!values ? (
|
||||
<Empty description="暂无数据" />
|
||||
) : (
|
||||
<>
|
||||
{/* 基本信息 */}
|
||||
<Card title="基本信息" bordered style={{ marginBottom: 16 }}>
|
||||
<Descriptions column={3} size="small" bordered>
|
||||
<Descriptions.Item label="姓名">{values.name || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">
|
||||
{sexMap[values.sex || ''] || values.sex || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="出生日期">{values.birthDate || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="手机号码">{values.phone || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历">
|
||||
{educationMap[values.education || ''] || values.education || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="政治面貌">
|
||||
{politicalMap[values.politicalAffiliation || ''] || values.politicalAffiliation || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="期望薪资">
|
||||
{values.salaryMin && values.salaryMax
|
||||
? `${values.salaryMin}-${values.salaryMax}`
|
||||
: '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="身份证号">{values.idCard || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="民族">{values.nation || '-'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
|
||||
{/* 匹配级别 */}
|
||||
{values.matchLevelDesc && (
|
||||
<Card title="匹配信息" bordered style={{ marginBottom: 16 }}>
|
||||
<Tag color="blue" style={{ fontSize: 14, padding: '4px 12px' }}>
|
||||
{values.matchLevelDesc}
|
||||
</Tag>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 技能列表 */}
|
||||
<Card title="技能信息" bordered style={{ marginBottom: 16 }}>
|
||||
<Spin spinning={loading}>
|
||||
{!skillList || skillList.length === 0 ? (
|
||||
<Empty description="暂无技能信息" />
|
||||
) : (
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={skillList}
|
||||
renderItem={(skill: API.ManagementList.RecommendSkill) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={skill.name || '未命名技能'}
|
||||
description={`等级: ${skill.levels || '未填写'}`}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
</Card>
|
||||
|
||||
{/* 工作经历 */}
|
||||
<Card title="工作经历" bordered style={{ marginBottom: 16 }}>
|
||||
<Spin spinning={loading}>
|
||||
{!experienceList || experienceList.length === 0 ? (
|
||||
<Empty description="暂无工作经历" />
|
||||
) : (
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
dataSource={experienceList}
|
||||
renderItem={(item: API.ManagementList.RecommendExperience, index: number) => (
|
||||
<List.Item style={{ marginBottom: 12 }}>
|
||||
<Card
|
||||
title={`经历 #${index + 1}`}
|
||||
bordered
|
||||
size="small"
|
||||
style={{ width: '100%', boxShadow: '0 1px 4px rgba(0,0,0,0.08)' }}
|
||||
>
|
||||
<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>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResumeDetail;
|
||||
247
src/pages/Management/List/ResumeRecommend/index.tsx
Normal file
247
src/pages/Management/List/ResumeRecommend/index.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useAccess, useParams } from '@umijs/max';
|
||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { Button, message } from 'antd';
|
||||
import { AlignLeftOutlined, SendOutlined } from '@ant-design/icons';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
import DictTag from '@/components/DictTag';
|
||||
import { getRecommendByJobId, getCmsJobIds } from '@/services/Management/list';
|
||||
import ResumeDetail from './ResumeDetail';
|
||||
import InterviewInvite from './InterviewInvite';
|
||||
|
||||
function ResumeRecommend() {
|
||||
const access = useAccess();
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
|
||||
const [educationEnum, setEducationEnum] = useState<any>([]);
|
||||
const [areaEnum, setAreaEnum] = useState<any>([]);
|
||||
const [sexEnum, setSexEnum] = useState<any>([]);
|
||||
const [politicalEnum, setPoliticalEnum] = useState<any>([]);
|
||||
const [currentRow, setCurrentRow] = useState<API.ManagementList.RecommendUser>();
|
||||
const [detailVisible, setDetailVisible] = useState<boolean>(false);
|
||||
const [inviteVisible, setInviteVisible] = useState<boolean>(false);
|
||||
const [jobInfo, setJobInfo] = useState<any>({});
|
||||
|
||||
const params = useParams();
|
||||
const jobId = params.id || '0';
|
||||
|
||||
useEffect(() => {
|
||||
getDictValueEnum('education', true, true).then((data) => {
|
||||
setEducationEnum(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);
|
||||
});
|
||||
getJobInfo(jobId);
|
||||
}, [jobId]);
|
||||
|
||||
const getJobInfo = async (id: string) => {
|
||||
try {
|
||||
const resData = await getCmsJobIds(id);
|
||||
if (resData.code === 200 && resData.data) {
|
||||
const data = resData.data as any;
|
||||
setJobInfo({
|
||||
jobTitle: data.jobTitle || '',
|
||||
companyId: data.companyId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取岗位信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const columns: ProColumns<API.ManagementList.RecommendUser>[] = [
|
||||
{
|
||||
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: 'phone',
|
||||
valueType: 'text',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
},
|
||||
{
|
||||
title: '匹配级别',
|
||||
dataIndex: 'matchLevelDesc',
|
||||
valueType: 'text',
|
||||
align: 'center',
|
||||
hideInSearch: true,
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
dataIndex: 'userId',
|
||||
width: 200,
|
||||
render: (_, record) => [
|
||||
<div key="actions" style={{ display: 'flex', justifyContent: 'center', gap: 8 }}>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="view-resume"
|
||||
icon={<AlignLeftOutlined />}
|
||||
onClick={() => {
|
||||
setCurrentRow(record);
|
||||
setDetailVisible(true);
|
||||
}}
|
||||
>
|
||||
查看详情
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="invite-interview"
|
||||
icon={<SendOutlined />}
|
||||
hidden={!access.hasPerms('cms:interview:add')}
|
||||
onClick={() => {
|
||||
setCurrentRow(record);
|
||||
setInviteVisible(true);
|
||||
}}
|
||||
>
|
||||
邀请面试
|
||||
</Button>
|
||||
</div>,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div style={{ width: '100%', float: 'right' }}>
|
||||
<ProTable<API.ManagementList.RecommendUser>
|
||||
actionRef={actionRef}
|
||||
rowKey="userId"
|
||||
key="index"
|
||||
headerTitle={`简历推荐${jobInfo.jobTitle ? ` - ${jobInfo.jobTitle}` : ''}`}
|
||||
columns={columns}
|
||||
request={async () => {
|
||||
try {
|
||||
const res = await getRecommendByJobId(jobId);
|
||||
if (res.code === 200) {
|
||||
const data = res.data || [];
|
||||
return {
|
||||
data,
|
||||
total: data.length,
|
||||
success: true,
|
||||
};
|
||||
} else {
|
||||
message.error(res.msg || '获取推荐简历失败');
|
||||
return {
|
||||
data: [],
|
||||
total: 0,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('获取推荐简历失败');
|
||||
return {
|
||||
data: [],
|
||||
total: 0,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}}
|
||||
search={false}
|
||||
pagination={{ pageSize: 10 }}
|
||||
/>
|
||||
</div>
|
||||
<ResumeDetail
|
||||
values={currentRow}
|
||||
open={detailVisible}
|
||||
jobId={jobId}
|
||||
onClose={() => {
|
||||
setDetailVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
}}
|
||||
/>
|
||||
<InterviewInvite
|
||||
open={inviteVisible}
|
||||
userId={currentRow?.userId}
|
||||
userName={currentRow?.name}
|
||||
jobId={jobId}
|
||||
companyId={jobInfo.companyId}
|
||||
jobName={jobInfo.jobTitle}
|
||||
onClose={(needRefresh) => {
|
||||
setInviteVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
if (needRefresh) {
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResumeRecommend;
|
||||
@@ -2,7 +2,7 @@ import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { FormattedMessage, useAccess, useParams } from '@umijs/max';
|
||||
import { Button, FormInstance, message, Tag } from 'antd';
|
||||
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
||||
import { FormOutlined, PlusOutlined,AlignLeftOutlined } from '@ant-design/icons';
|
||||
import { FormOutlined, PlusOutlined, AlignLeftOutlined, SendOutlined } from '@ant-design/icons';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
import DictTag from '@/components/DictTag';
|
||||
import { exportCmsAppUserExport } from '@/services/mobileusers/list';
|
||||
@@ -15,6 +15,7 @@ import similarityJobs from '@/utils/similarity_Job';
|
||||
import Detail from './detail';
|
||||
import Hire from './hire';
|
||||
import ResumeView from './resumeView';
|
||||
import InterviewInvite from '../ResumeRecommend/InterviewInvite';
|
||||
|
||||
|
||||
const handleExport = async (values: API.MobileUser.ListParams) => {
|
||||
@@ -51,6 +52,7 @@ function ManagementList() {
|
||||
const [jobInfo, setJobInfo] = useState({});
|
||||
const [matchingDegree, setMatchingDegree] = useState<any>(null);
|
||||
const [resumeVisible, setResumeVisible] = useState<boolean>(false);
|
||||
const [inviteVisible, setInviteVisible] = useState<boolean>(false);
|
||||
const [hireAdd, setHireAdd] = useState<any>(null);
|
||||
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
|
||||
const params = useParams();
|
||||
@@ -191,7 +193,7 @@ function ManagementList() {
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
dataIndex: 'jobId',
|
||||
width: 200,
|
||||
width: 280,
|
||||
render: (jobId, record) => [
|
||||
<div key="first-row" style={{marginBottom: 8, display: 'flex', justifyContent: 'center'}}>
|
||||
<Button
|
||||
@@ -210,7 +212,20 @@ function ManagementList() {
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="edit"
|
||||
key="invite-interview"
|
||||
icon={<SendOutlined/>}
|
||||
hidden={!access.hasPerms('cms:interview:add')}
|
||||
onClick={() => {
|
||||
setCurrentRow(record);
|
||||
setInviteVisible(true);
|
||||
}}
|
||||
>
|
||||
邀请面试
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="hire"
|
||||
icon={<FormOutlined/>}
|
||||
hidden={!access.hasPerms('cms:employeeConfirm:add')}
|
||||
onClick={() => {
|
||||
@@ -224,9 +239,8 @@ function ManagementList() {
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="edit"
|
||||
key="match-detail"
|
||||
icon={<AlignLeftOutlined/>}
|
||||
// hidden={!access.hasPerms('area:business:List.update')}
|
||||
onClick={() => {
|
||||
setModalVisible(true);
|
||||
setCurrentRow(record);
|
||||
@@ -323,6 +337,21 @@ function ManagementList() {
|
||||
setCurrentRow(undefined);
|
||||
}}
|
||||
/>
|
||||
<InterviewInvite
|
||||
open={inviteVisible}
|
||||
userId={currentRow?.userId}
|
||||
userName={currentRow?.name}
|
||||
jobId={jobId}
|
||||
companyId={(jobInfo as any)?.companyId}
|
||||
jobName={(jobInfo as any)?.jobTitle}
|
||||
onClose={(needRefresh) => {
|
||||
setInviteVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
if (needRefresh) {
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {Modal, Card, Descriptions, List, Button, Divider, Form, Select, Input, DatePicker, message} from 'antd';
|
||||
import React, { useEffect, useRef,useState } from 'react';
|
||||
import { ProDescriptions} from '@ant-design/pro-components';
|
||||
import { Modal, Card, Descriptions, List, Button, Form, Select, Input, DatePicker, message, Empty, Spin } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
addCmsUserWorkExperiencesList,
|
||||
addCmsInterview,
|
||||
getCmsInterviewList,
|
||||
getAppSkillList,
|
||||
} from '@/services/Management/list';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@@ -18,11 +18,11 @@ export type ListFormProps = {
|
||||
matching?: any;
|
||||
};
|
||||
|
||||
const listEdit: React.FC<ListFormProps> = (props) => {
|
||||
const ResumeViewModal: React.FC<ListFormProps> = (props) => {
|
||||
|
||||
const mapRef = useRef(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [contactInfoList, setContactInfoList] = useState<API.Management.WorkExperence[]>([]);
|
||||
const [experienceList, setExperienceList] = useState<any[]>([]);
|
||||
const [skillList, setSkillList] = useState<any[]>([]);
|
||||
|
||||
// 面试邀约相关状态
|
||||
const [interviewForm] = Form.useForm();
|
||||
@@ -31,16 +31,41 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
||||
const [alreadyInvited, setAlreadyInvited] = useState<boolean>(false);
|
||||
const [checkingInvitation, setCheckingInvitation] = useState<boolean>(false);
|
||||
|
||||
// 性别映射
|
||||
const sexMap: Record<string, string> = {
|
||||
'0': '男',
|
||||
'1': '女',
|
||||
};
|
||||
|
||||
// 学历映射
|
||||
const educationMap: Record<string, string> = {
|
||||
'1': '初中及以下',
|
||||
'2': '高中',
|
||||
'3': '中专',
|
||||
'4': '大专',
|
||||
'5': '本科',
|
||||
'6': '硕士',
|
||||
'7': '博士',
|
||||
};
|
||||
|
||||
// 政治面貌映射
|
||||
const politicalMap: Record<string, string> = {
|
||||
'1': '中共党员',
|
||||
'2': '中共预备党员',
|
||||
'3': '共青团员',
|
||||
'4': '群众',
|
||||
'5': '民主党派',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props.open && props.values) {
|
||||
console.log(props.values);
|
||||
fetchResumeDetail();
|
||||
fetchSkills();
|
||||
checkExistingInvitation();
|
||||
// 重置面试邀约表单
|
||||
interviewForm.resetFields();
|
||||
setInterviewMethod('online');
|
||||
}
|
||||
}, [props.values,props.open]);
|
||||
}, [props.values, props.open]);
|
||||
|
||||
// 检查是否已存在有效邀约
|
||||
const checkExistingInvitation = async () => {
|
||||
@@ -52,7 +77,6 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
||||
userId: props.values.userId,
|
||||
});
|
||||
if (res?.code === 200 && res?.rows?.length > 0) {
|
||||
// 存在非 rejected 的邀约即为已邀约
|
||||
const hasActive = res.rows.some(
|
||||
(item: API.Management.InterviewInvitation) => item.status !== 'rejected'
|
||||
);
|
||||
@@ -67,13 +91,12 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
||||
|
||||
const fetchResumeDetail = async () => {
|
||||
if (!props.values?.userId) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const parm={userId: props.values.userId, jobId: props.matching?.jobId};
|
||||
const parm = { userId: props.values.userId, jobId: props.matching?.jobId };
|
||||
const response = await addCmsUserWorkExperiencesList(parm);
|
||||
if (response.code === 200) {
|
||||
setContactInfoList(response.rows);
|
||||
setExperienceList(response.rows);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取经历详情失败:', error);
|
||||
@@ -82,6 +105,18 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchSkills = async () => {
|
||||
if (!props.values?.userId) return;
|
||||
try {
|
||||
const response = await getAppSkillList({ userId: props.values.userId });
|
||||
if (response.code === 200) {
|
||||
setSkillList(response.rows || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取技能信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 发送面试邀约
|
||||
const handleInterviewSubmit = async () => {
|
||||
try {
|
||||
@@ -125,162 +160,200 @@ const listEdit: React.FC<ListFormProps> = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const values = props.values;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={props.open}
|
||||
onCancel={() => props.onClose()}
|
||||
title="简历详情"
|
||||
width={800}
|
||||
width={900}
|
||||
footer={[
|
||||
<Button key="cancel" onClick={() => props.onClose()}>
|
||||
取消
|
||||
</Button>
|
||||
</Button>,
|
||||
]}
|
||||
destroyOnClose
|
||||
>
|
||||
<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 (
|
||||
{!values ? (
|
||||
<Empty description="暂无数据" />
|
||||
) : (
|
||||
<>
|
||||
{/* 基本信息 */}
|
||||
<Card title="基本信息" bordered style={{ marginBottom: 16 }}>
|
||||
<Descriptions column={3} size="small" bordered>
|
||||
<Descriptions.Item label="姓名">{values.name || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="性别">
|
||||
{sexMap[values.sex || ''] || values.sex || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="出生日期">{values.birthDate || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="手机号码">{values.phone || '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="学历">
|
||||
{educationMap[values.education || ''] || values.education || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="政治面貌">
|
||||
{politicalMap[values.politicalAffiliation || ''] || values.politicalAffiliation || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="期望薪资">
|
||||
{values.salaryMin && values.salaryMax
|
||||
? `${values.salaryMin}-${values.salaryMax}`
|
||||
: '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="身份证号">{values.idCard || '-'}</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
|
||||
{/* 技能列表 */}
|
||||
{skillList.length > 0 && (
|
||||
<Card title="技能信息" bordered style={{ marginBottom: 16 }}>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={skillList}
|
||||
renderItem={(skill: any) => (
|
||||
<List.Item>
|
||||
<List.Item.Meta
|
||||
title={skill.name || '未命名技能'}
|
||||
description={`等级: ${skill.levels || '未填写'}`}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 工作经历 */}
|
||||
<Card title="工作经历" bordered style={{ marginBottom: 16 }}>
|
||||
<Spin spinning={loading}>
|
||||
{!experienceList || experienceList.length === 0 ? (
|
||||
<Empty description="暂无工作经历" />
|
||||
) : (
|
||||
<List
|
||||
itemLayout="vertical"
|
||||
dataSource={contactInfoList}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item
|
||||
style={{ marginBottom: 16 }}
|
||||
>
|
||||
dataSource={experienceList}
|
||||
renderItem={(item: any, index: number) => (
|
||||
<List.Item style={{ marginBottom: 12 }}>
|
||||
<Card
|
||||
title={`经历信息 #${index + 1}`}
|
||||
title={`经历 #${index + 1}`}
|
||||
bordered
|
||||
style={{ width: '100%', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }}
|
||||
size="small"
|
||||
style={{ width: '100%', boxShadow: '0 1px 4px rgba(0,0,0,0.08)' }}
|
||||
>
|
||||
<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 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>
|
||||
)}
|
||||
</Spin>
|
||||
</Card>
|
||||
|
||||
<Divider />
|
||||
<Card title="面试邀约" bordered style={{ marginTop: 16 }}>
|
||||
<Form
|
||||
form={interviewForm}
|
||||
layout="vertical"
|
||||
initialValues={{ interviewMethod: 'online', meetingLink: 'https://mc.repohub.cn/pro.html' }}
|
||||
>
|
||||
<Form.Item
|
||||
name="interviewMethod"
|
||||
label="面试方式"
|
||||
rules={[{ required: true, message: '请选择面试方式' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择面试方式"
|
||||
onChange={(value) => setInterviewMethod(value)}
|
||||
{/* 面试邀约 */}
|
||||
<Card title="面试邀约" bordered>
|
||||
<Form
|
||||
form={interviewForm}
|
||||
layout="vertical"
|
||||
initialValues={{ interviewMethod: 'online', meetingLink: 'https://mc.repohub.cn/pro.html' }}
|
||||
>
|
||||
<Option value="online">线上</Option>
|
||||
<Option value="offline">线下</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="interviewTime"
|
||||
label="面试时间"
|
||||
rules={[{ required: true, message: '请选择面试时间' }]}
|
||||
>
|
||||
<DatePicker
|
||||
showTime
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请选择面试时间"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{interviewMethod === 'online' && (
|
||||
<>
|
||||
<Form.Item
|
||||
name="meetingLink"
|
||||
label="在线会议链接"
|
||||
name="interviewMethod"
|
||||
label="面试方式"
|
||||
rules={[{ required: true, message: '请选择面试方式' }]}
|
||||
>
|
||||
<Input placeholder="https://mc.repohub.cn/pro.html" disabled />
|
||||
<Select
|
||||
placeholder="请选择面试方式"
|
||||
onChange={(value) => setInterviewMethod(value)}
|
||||
>
|
||||
<Option value="online">线上</Option>
|
||||
<Option value="offline">线下</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
{/* <Form.Item
|
||||
name="meetingPassword"
|
||||
label="会议密码"
|
||||
rules={[{ required: true, message: '请输入会议密码' }]}
|
||||
|
||||
<Form.Item
|
||||
name="interviewTime"
|
||||
label="面试时间"
|
||||
rules={[{ required: true, message: '请选择面试时间' }]}
|
||||
>
|
||||
<Input placeholder="请输入会议密码" />
|
||||
</Form.Item> */}
|
||||
</>
|
||||
)}
|
||||
<DatePicker
|
||||
showTime
|
||||
style={{ width: '100%' }}
|
||||
placeholder="请选择面试时间"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{interviewMethod === 'offline' && (
|
||||
<Form.Item
|
||||
name="interviewLocation"
|
||||
label="面试地点"
|
||||
rules={[{ required: true, message: '请输入面试地点' }]}
|
||||
>
|
||||
<Input placeholder="请输入面试地点" />
|
||||
</Form.Item>
|
||||
)}
|
||||
{interviewMethod === 'online' && (
|
||||
<Form.Item
|
||||
name="meetingLink"
|
||||
label="在线会议链接"
|
||||
>
|
||||
<Input placeholder="请输入在线会议链接" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="remark"
|
||||
label="备注"
|
||||
>
|
||||
<TextArea rows={3} placeholder="选填,如有其他注意事项可在此说明" />
|
||||
</Form.Item>
|
||||
{interviewMethod === 'offline' && (
|
||||
<Form.Item
|
||||
name="interviewLocation"
|
||||
label="面试地点"
|
||||
rules={[{ required: true, message: '请输入面试地点' }]}
|
||||
>
|
||||
<Input placeholder="请输入面试地点" />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item
|
||||
name="interviewerName"
|
||||
label="面试官姓名"
|
||||
rules={[{ required: true, message: '请输入面试官姓名' }]}
|
||||
>
|
||||
<Input placeholder="请输入面试官姓名" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="remark"
|
||||
label="备注"
|
||||
>
|
||||
<TextArea rows={3} placeholder="选填,如有其他注意事项可在此说明" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="contactPhone"
|
||||
label="面试官联系方式"
|
||||
rules={[{ required: true, message: '请输入面试官联系方式' }]}
|
||||
>
|
||||
<Input placeholder="请输入手机号或微信等联系方式" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="interviewerName"
|
||||
label="面试官姓名"
|
||||
rules={[{ required: true, message: '请输入面试官姓名' }]}
|
||||
>
|
||||
<Input placeholder="请输入面试官姓名" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={submitting || checkingInvitation}
|
||||
disabled={alreadyInvited}
|
||||
onClick={handleInterviewSubmit}
|
||||
>
|
||||
{alreadyInvited ? '已邀约' : '发送面试邀约'}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
<Form.Item
|
||||
name="contactPhone"
|
||||
label="面试官联系方式"
|
||||
rules={[{ required: true, message: '请输入面试官联系方式' }]}
|
||||
>
|
||||
<Input placeholder="请输入手机号或微信等联系方式" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
loading={submitting || checkingInvitation}
|
||||
disabled={alreadyInvited}
|
||||
onClick={handleInterviewSubmit}
|
||||
>
|
||||
{alreadyInvited ? '已邀约' : '发送面试邀约'}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default listEdit;
|
||||
export default ResumeViewModal;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} 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, UploadOutlined } from '@ant-design/icons';
|
||||
import { AlignLeftOutlined, BarChartOutlined, DeleteOutlined, FormOutlined, PlusOutlined, DownloadOutlined, UploadOutlined, IdcardOutlined } from '@ant-design/icons';
|
||||
import EditManageRow from './edit';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
import DictTag from '@/components/DictTag';
|
||||
@@ -307,7 +307,7 @@ function ManagementList() {
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
dataIndex: 'jobId',
|
||||
width: 300,
|
||||
width: 380,
|
||||
render: (jobId, record) => [
|
||||
<div key="first-row" style={{ marginBottom: 8, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button
|
||||
@@ -327,13 +327,23 @@ function ManagementList() {
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="edit"
|
||||
key="candidates"
|
||||
icon={<BarChartOutlined />}
|
||||
hidden={!access.hasPerms('bussiness:job:candidates')}
|
||||
onClick={() => history.push(`/management/see-matching/index/${record.jobId}`)}
|
||||
>
|
||||
查看申请人
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="recommend"
|
||||
icon={<IdcardOutlined />}
|
||||
hidden={!access.hasPerms('bussiness:job:candidates')}
|
||||
onClick={() => history.push(`/management/resume-recommend/index/${record.jobId}`)}
|
||||
>
|
||||
简历推荐
|
||||
</Button>
|
||||
</div>,
|
||||
<div key="second-row" style={{ display: 'flex', justifyContent: 'space-evenly'}}>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user