2026-07-16 19:34:40 +08:00
|
|
|
|
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
2026-07-20 10:22:51 +08:00
|
|
|
|
import { useAccess, useNavigate } from '@umijs/max';
|
2026-07-16 19:34:40 +08:00
|
|
|
|
import { Button, FormInstance, message, Tag } from 'antd';
|
|
|
|
|
|
import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
|
2026-07-20 10:22:51 +08:00
|
|
|
|
import { StarFilled, FileTextOutlined, SendOutlined } from '@ant-design/icons';
|
2026-07-16 19:34:40 +08:00
|
|
|
|
import { getDictValueEnum } from '@/services/system/dict';
|
|
|
|
|
|
import DictTag from '@/components/DictTag';
|
|
|
|
|
|
import { delHrTalentCollect, getHrTalentCollectList } from '@/services/mobileusers/list';
|
2026-07-20 10:22:51 +08:00
|
|
|
|
import InterviewInvite from '@/pages/Management/List/ResumeRecommend/InterviewInvite';
|
2026-07-16 19:34:40 +08:00
|
|
|
|
|
|
|
|
|
|
function TalentCollectList() {
|
|
|
|
|
|
const access = useAccess();
|
2026-07-20 10:22:51 +08:00
|
|
|
|
const navigate = useNavigate();
|
2026-07-16 19:34:40 +08:00
|
|
|
|
|
|
|
|
|
|
const formTableRef = useRef<FormInstance>();
|
|
|
|
|
|
const actionRef = useRef<ActionType>();
|
|
|
|
|
|
|
|
|
|
|
|
const [educationEnum, setEducationEnum] = useState<any>({});
|
|
|
|
|
|
const [areaEnum, setAreaEnum] = useState<any>({});
|
|
|
|
|
|
const [sexEnum, setSexEnum] = useState<any>({});
|
2026-07-20 10:22:51 +08:00
|
|
|
|
const [inviteVisible, setInviteVisible] = useState<boolean>(false);
|
|
|
|
|
|
const [currentRow, setCurrentRow] = useState<API.TalentCollect.ListRow>();
|
2026-07-16 19:34:40 +08:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-07-16 20:15:25 +08:00
|
|
|
|
getDictValueEnum('education', false, true).then((data) => setEducationEnum(data));
|
|
|
|
|
|
getDictValueEnum('area', false, true).then((data) => setAreaEnum(data));
|
2026-07-16 19:34:40 +08:00
|
|
|
|
getDictValueEnum('sys_user_sex', false).then((data) => setSexEnum(data));
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
2026-07-20 10:22:51 +08:00
|
|
|
|
// 取消收藏(接口路径参数为收藏记录主键id)
|
2026-07-16 19:34:40 +08:00
|
|
|
|
const handleCancelCollect = async (record: API.TalentCollect.ListRow) => {
|
2026-07-20 10:22:51 +08:00
|
|
|
|
if (!record.id) return;
|
2026-07-16 19:34:40 +08:00
|
|
|
|
try {
|
2026-07-20 10:22:51 +08:00
|
|
|
|
const res = await delHrTalentCollect(String(record.id));
|
2026-07-16 19:34:40 +08:00
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
|
message.success('已取消收藏');
|
|
|
|
|
|
actionRef.current?.reload();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
message.error(res.msg || '取消收藏失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
|
message.error(error?.response?.data?.msg || error?.message || '取消收藏失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const intentionLevelMap: Record<string, { text: string; color: string }> = {
|
|
|
|
|
|
'1': { text: '高意向', color: 'red' },
|
|
|
|
|
|
'2': { text: '中等意向', color: 'blue' },
|
|
|
|
|
|
'3': { text: '低意向', color: 'default' },
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const sourceTypeMap: Record<string, string> = {
|
|
|
|
|
|
'1': '简历搜索',
|
|
|
|
|
|
'2': '投递记录',
|
|
|
|
|
|
'3': '邀约面试',
|
|
|
|
|
|
'4': '手动录入',
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const columns: ProColumns<API.TalentCollect.ListRow>[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '用户名',
|
|
|
|
|
|
dataIndex: 'name',
|
|
|
|
|
|
valueType: 'text',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '联系方式',
|
|
|
|
|
|
dataIndex: 'phone',
|
|
|
|
|
|
valueType: 'text',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '意向等级',
|
|
|
|
|
|
dataIndex: 'intentionLevel',
|
|
|
|
|
|
valueType: 'select',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
valueEnum: {
|
|
|
|
|
|
'1': '高意向',
|
|
|
|
|
|
'2': '中等意向',
|
|
|
|
|
|
'3': '低意向',
|
|
|
|
|
|
},
|
|
|
|
|
|
render: (_, record) => {
|
|
|
|
|
|
const info = intentionLevelMap[record.intentionLevel || ''];
|
|
|
|
|
|
if (!info) return '-';
|
|
|
|
|
|
return <Tag color={info.color}>{info.text}</Tag>;
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '人才标签',
|
|
|
|
|
|
dataIndex: 'collectTags',
|
|
|
|
|
|
valueType: 'text',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
render: (_, record) => {
|
|
|
|
|
|
if (!record.collectTags) return '-';
|
|
|
|
|
|
const tags = record.collectTags.split(',').filter(Boolean);
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
{tags.map((tag) => (
|
|
|
|
|
|
<Tag key={tag} color="blue">
|
|
|
|
|
|
{tag.trim()}
|
|
|
|
|
|
</Tag>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '收藏来源',
|
|
|
|
|
|
dataIndex: 'sourceType',
|
|
|
|
|
|
valueType: 'select',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
valueEnum: {
|
|
|
|
|
|
'1': '简历搜索',
|
|
|
|
|
|
'2': '投递记录',
|
|
|
|
|
|
'3': '邀约面试',
|
|
|
|
|
|
'4': '手动录入',
|
|
|
|
|
|
},
|
|
|
|
|
|
render: (_, record) => sourceTypeMap[record.sourceType || ''] || '-',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
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: 'createTime',
|
|
|
|
|
|
valueType: 'dateTime',
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '操作',
|
|
|
|
|
|
hideInSearch: true,
|
|
|
|
|
|
align: 'center',
|
|
|
|
|
|
dataIndex: 'userId',
|
2026-07-20 10:22:51 +08:00
|
|
|
|
width: 280,
|
2026-07-16 19:34:40 +08:00
|
|
|
|
render: (_, record) => [
|
2026-07-20 10:22:51 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
key="resume"
|
|
|
|
|
|
icon={<FileTextOutlined />}
|
|
|
|
|
|
onClick={() => navigate(`/usermgmt/appuser/resume/${record.userId}?readOnly=1`)}
|
|
|
|
|
|
>
|
|
|
|
|
|
简历
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
key="invite-interview"
|
|
|
|
|
|
icon={<SendOutlined />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:interview:add')}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
setCurrentRow(record);
|
|
|
|
|
|
setInviteVisible(true);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
邀请面试
|
|
|
|
|
|
</Button>,
|
2026-07-16 19:34:40 +08:00
|
|
|
|
<Button
|
|
|
|
|
|
type="link"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
key="cancel-collect"
|
|
|
|
|
|
icon={<StarFilled style={{ color: '#faad14' }} />}
|
|
|
|
|
|
hidden={!access.hasPerms('cms:hrTalentCollect:remove')}
|
|
|
|
|
|
onClick={() => handleCancelCollect(record)}
|
|
|
|
|
|
>
|
|
|
|
|
|
取消收藏
|
|
|
|
|
|
</Button>,
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Fragment>
|
|
|
|
|
|
<div style={{ width: '100%', float: 'right' }}>
|
|
|
|
|
|
<ProTable<API.TalentCollect.ListRow>
|
|
|
|
|
|
search={{ labelWidth: 'auto' }}
|
|
|
|
|
|
actionRef={actionRef}
|
|
|
|
|
|
formRef={formTableRef}
|
|
|
|
|
|
rowKey="id"
|
|
|
|
|
|
key="talentCollect"
|
|
|
|
|
|
columns={columns}
|
|
|
|
|
|
request={(params) => {
|
|
|
|
|
|
const { current, pageSize, ...searchParams } = params;
|
|
|
|
|
|
const filteredParams: Record<string, any> = {};
|
|
|
|
|
|
Object.keys(searchParams).forEach((key) => {
|
|
|
|
|
|
const val = (searchParams as any)[key];
|
|
|
|
|
|
if (val !== undefined && val !== null && val !== '') {
|
|
|
|
|
|
filteredParams[key] = val;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return getHrTalentCollectList({
|
|
|
|
|
|
pageNum: current,
|
|
|
|
|
|
pageSize,
|
|
|
|
|
|
...filteredParams,
|
|
|
|
|
|
}).then((res: any) => {
|
|
|
|
|
|
const result = {
|
|
|
|
|
|
data: res.rows,
|
|
|
|
|
|
total: res.total,
|
|
|
|
|
|
success: true,
|
|
|
|
|
|
};
|
|
|
|
|
|
return result;
|
|
|
|
|
|
});
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-07-20 10:22:51 +08:00
|
|
|
|
<InterviewInvite
|
|
|
|
|
|
open={inviteVisible}
|
|
|
|
|
|
userId={currentRow?.userId}
|
|
|
|
|
|
userName={currentRow?.name}
|
|
|
|
|
|
companyId={currentRow?.companyId}
|
|
|
|
|
|
onClose={(needRefresh) => {
|
|
|
|
|
|
setInviteVisible(false);
|
|
|
|
|
|
setCurrentRow(undefined);
|
|
|
|
|
|
if (needRefresh) {
|
|
|
|
|
|
actionRef.current?.reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2026-07-16 19:34:40 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</Fragment>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default TalentCollectList;
|