简历筛选功能开发
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
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
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:
@@ -17,9 +17,9 @@ export default {
|
||||
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
|
||||
'/api/': {
|
||||
// 要代理的地址
|
||||
target: localBackendUrl,
|
||||
// target: localBackendUrl,
|
||||
// target: 'http://39.98.44.136:6024/api/shihezi/', // 线上环境
|
||||
// target: 'http://test.xjshzly.longbiosphere.com/api/shihezi/', // 测试环境
|
||||
target: 'https://test.xjshzly.longbiosphere.com/api/shihezi/', // 测试环境
|
||||
// target: 'http://wykj.cdwsx.com/api',// 后端
|
||||
// target: 'http://ks.zhaopinzao8dian.com/api/ks',
|
||||
// 配置了这个可以从 http 代理到 https
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
Progress,
|
||||
Tag,
|
||||
Spin,
|
||||
Divider
|
||||
Divider,
|
||||
Switch,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
UserOutlined,
|
||||
@@ -24,7 +26,7 @@ import { history } from '@umijs/max';
|
||||
import JobPortalHeader from '@/components/JobPortalHeader';
|
||||
import useAppUserModel from '@/models/jobportal/user';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
import { getMyTj } from '@/services/jobportal/user';
|
||||
import { getMyTj, changeResumeStatus } from '@/services/jobportal/user';
|
||||
import defaultAvatar from '@/assets/avatar.jpg';
|
||||
import './index.less';
|
||||
|
||||
@@ -51,7 +53,7 @@ const calculateResumeCompleteness = (userInfo: API.AppUserInfo | null): number =
|
||||
};
|
||||
|
||||
const PersonalCenter: React.FC = () => {
|
||||
const { userInfo, loading, fetchUserInfo } = useAppUserModel();
|
||||
const { userInfo, loading, fetchUserInfo, updateUserInfo } = useAppUserModel();
|
||||
const [dictData, setDictData] = useState<{
|
||||
sex?: Record<string, any>;
|
||||
education?: Record<string, any>;
|
||||
@@ -166,6 +168,25 @@ const PersonalCenter: React.FC = () => {
|
||||
return !!(userInfo?.idCard && userInfo.idCard.trim());
|
||||
}, [userInfo?.idCard]);
|
||||
|
||||
const handleResumeStatusChange = async (checked: boolean) => {
|
||||
if (!userInfo?.userId) return;
|
||||
const newStatus = checked ? '1' : '2';
|
||||
try {
|
||||
const response = await changeResumeStatus({
|
||||
userId: userInfo.userId,
|
||||
resumeStatus: newStatus,
|
||||
});
|
||||
if (response?.code === 200) {
|
||||
// 同步更新 React 状态和本地缓存
|
||||
const updatedInfo = { ...userInfo, resumeStatus: newStatus };
|
||||
updateUserInfo(updatedInfo);
|
||||
message.success(checked ? '简历已公开' : '简历已设为不公开');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('修改简历公开状态失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const services = useMemo(() => [
|
||||
{
|
||||
id: 1,
|
||||
@@ -193,15 +214,6 @@ const PersonalCenter: React.FC = () => {
|
||||
color: '#1677ff',
|
||||
link: '/job-portal/personal-center/interviews'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '在线面试',
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
status: '',
|
||||
hasArrow: true,
|
||||
color: '#1890ff',
|
||||
link: 'https://mc.repohub.cn/pro.html'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '通知与提醒',
|
||||
@@ -209,8 +221,19 @@ const PersonalCenter: React.FC = () => {
|
||||
status: '已开启',
|
||||
hasArrow: false,
|
||||
color: '#fa8c16'
|
||||
}
|
||||
], [isRealNameVerified]);
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '公开简历',
|
||||
icon: <FileTextOutlined />,
|
||||
hasArrow: false,
|
||||
color: '#1677ff',
|
||||
switchConfig: {
|
||||
checked: userInfo?.resumeStatus !== '2',
|
||||
onChange: handleResumeStatusChange,
|
||||
}
|
||||
},
|
||||
], [isRealNameVerified, userInfo?.resumeStatus]);
|
||||
|
||||
const handleServiceClick = (service: any) => {
|
||||
if (service.link) {
|
||||
@@ -348,7 +371,15 @@ const PersonalCenter: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{service.hasArrow && <RightOutlined className="service-arrow" />}
|
||||
{service.switchConfig ? (
|
||||
<Switch
|
||||
checked={service.switchConfig.checked}
|
||||
onChange={service.switchConfig.onChange}
|
||||
onClick={(_, e) => e.stopPropagation()}
|
||||
/>
|
||||
) : service.hasArrow ? (
|
||||
<RightOutlined className="service-arrow" />
|
||||
) : null}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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, SendOutlined } from '@ant-design/icons';
|
||||
import { FormOutlined, PlusOutlined, AlignLeftOutlined, SendOutlined, FilterOutlined } from '@ant-design/icons';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
import DictTag from '@/components/DictTag';
|
||||
import { exportCmsAppUserExport } from '@/services/mobileusers/list';
|
||||
@@ -16,6 +16,7 @@ import Detail from './detail';
|
||||
import Hire from './hire';
|
||||
import ResumeView from './resumeView';
|
||||
import InterviewInvite from '../ResumeRecommend/InterviewInvite';
|
||||
import ResumeFilter from './resumeFilter';
|
||||
|
||||
|
||||
const handleExport = async (values: API.MobileUser.ListParams) => {
|
||||
@@ -53,6 +54,7 @@ function ManagementList() {
|
||||
const [matchingDegree, setMatchingDegree] = useState<any>(null);
|
||||
const [resumeVisible, setResumeVisible] = useState<boolean>(false);
|
||||
const [inviteVisible, setInviteVisible] = useState<boolean>(false);
|
||||
const [filterVisible, setFilterVisible] = useState<boolean>(false);
|
||||
const [hireAdd, setHireAdd] = useState<any>(null);
|
||||
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
|
||||
const params = useParams();
|
||||
@@ -250,6 +252,21 @@ function ManagementList() {
|
||||
>
|
||||
查看匹配详情
|
||||
</Button>
|
||||
</div>,
|
||||
<div key="second-row" style={{display: 'flex', justifyContent: 'center'}}>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
key="resume-filter"
|
||||
icon={<FilterOutlined/>}
|
||||
hidden={!access.hasPerms('cms:jobApply:filterQzz')}
|
||||
onClick={() => {
|
||||
setCurrentRow(record);
|
||||
setFilterVisible(true);
|
||||
}}
|
||||
>
|
||||
简历筛选
|
||||
</Button>
|
||||
</div>
|
||||
],
|
||||
},
|
||||
@@ -353,6 +370,18 @@ function ManagementList() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<ResumeFilter
|
||||
open={filterVisible}
|
||||
applyId={currentRow?.applyId}
|
||||
userName={currentRow?.name}
|
||||
onClose={(needRefresh) => {
|
||||
setFilterVisible(false);
|
||||
setCurrentRow(undefined);
|
||||
if (needRefresh) {
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
119
src/pages/Management/List/SeeMatching/resumeFilter.tsx
Normal file
119
src/pages/Management/List/SeeMatching/resumeFilter.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import { Modal, Form, Radio, Select, message } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { filterQzz } from '@/services/Management/list';
|
||||
import { getDictValueEnum } from '@/services/system/dict';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
export type ResumeFilterProps = {
|
||||
onClose: (needRefresh?: boolean) => void;
|
||||
open: boolean;
|
||||
applyId?: string;
|
||||
userName?: string;
|
||||
};
|
||||
|
||||
const ResumeFilter: React.FC<ResumeFilterProps> = (props) => {
|
||||
const { open, applyId, userName } = props;
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [submitting, setSubmitting] = useState<boolean>(false);
|
||||
const [filterStatus, setFilterStatus] = useState<string>('0');
|
||||
const [reasonEnum, setReasonEnum] = useState<any>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
form.resetFields();
|
||||
setFilterStatus('0');
|
||||
// 加载不通过筛选原因的字典
|
||||
getDictValueEnum('resume_file_type', false, true).then((data) => {
|
||||
setReasonEnum(data);
|
||||
});
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
setSubmitting(true);
|
||||
|
||||
const payload: { id: string; filterStatus: string; filterReason?: string } = {
|
||||
id: applyId || '',
|
||||
filterStatus: values.filterStatus,
|
||||
};
|
||||
|
||||
// 如果不通过筛选,拼接原因
|
||||
if (values.filterStatus === '2' && values.filterReason && values.filterReason.length > 0) {
|
||||
payload.filterReason = values.filterReason.join('、');
|
||||
}
|
||||
|
||||
const response = await filterQzz(payload);
|
||||
if (response.code === 200) {
|
||||
message.success('简历筛选完成');
|
||||
form.resetFields();
|
||||
props.onClose(true);
|
||||
} 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={`简历筛选${userName ? ` - ${userName}` : ''}`}
|
||||
width={520}
|
||||
onOk={handleSubmit}
|
||||
confirmLoading={submitting}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{ filterStatus: '0' }}
|
||||
>
|
||||
<Form.Item
|
||||
name="filterStatus"
|
||||
label="筛选结果"
|
||||
rules={[{ required: true, message: '请选择筛选结果' }]}
|
||||
>
|
||||
<Radio.Group
|
||||
onChange={(e) => setFilterStatus(e.target.value)}
|
||||
>
|
||||
<Radio value="0">通过筛选</Radio>
|
||||
<Radio value="2">不通过筛选</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
|
||||
{filterStatus === '2' && (
|
||||
<Form.Item
|
||||
name="filterReason"
|
||||
label="不通过原因"
|
||||
rules={[{ required: true, message: '请选择或输入不通过原因' }]}
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder="请选择或输入不通过原因,按回车添加"
|
||||
style={{ width: '100%' }}
|
||||
tokenSeparators={['、', ',', ',']}
|
||||
>
|
||||
{Object.keys(reasonEnum).map((key) => (
|
||||
<Option key={key} value={reasonEnum[key].text}>
|
||||
{reasonEnum[key].text}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResumeFilter;
|
||||
@@ -158,6 +158,14 @@ export async function getAppSkillList(params?: { userId?: number }) {
|
||||
|
||||
// ========== 面试消息角标 APIs ==========
|
||||
|
||||
/** 简历筛选 */
|
||||
export async function filterQzz(params?: { id: string; filterStatus: string; filterReason?: string }) {
|
||||
return request<API.Management.ManagePageResult>(`/api/cms/jobApply/filterQzz`, {
|
||||
method: 'PUT',
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取面试消息角标数量(后台管理端菜单角标) */
|
||||
export async function getInterviewBadgeCount() {
|
||||
return request<{
|
||||
|
||||
@@ -182,6 +182,15 @@ export async function submitJobComplaint(params: {
|
||||
});
|
||||
}
|
||||
|
||||
/** 修改简历公开状态 POST /api/cms/appUser/changeResumeStatus */
|
||||
export async function changeResumeStatus(params: { userId: number; resumeStatus: string }, options?: Record<string, any>) {
|
||||
return request<API.Result>('/api/cms/appUser/changeResumeStatus', {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
...(options || {}),
|
||||
});
|
||||
}
|
||||
|
||||
/** 屏蔽企业 POST /cms/blockCompany */
|
||||
export async function blockCompany(params: { userId: number; companyId: number }) {
|
||||
return request<API.Result>('/api/cms/blockCompany', {
|
||||
|
||||
Reference in New Issue
Block a user