diff --git a/config/routes.ts b/config/routes.ts index 8df8495..48a75d5 100644 --- a/config/routes.ts +++ b/config/routes.ts @@ -80,6 +80,10 @@ export default [ path: '/job-portal/personal-center/showcase',// 我的风采 component: './JobPortal/PersonalCenter/Showcase', }, + { + path: '/job-portal/personal-center/interviews',// 我的面试邀约 + component: './JobPortal/PersonalCenter/Interviews', + }, { path: '/job-portal/message',// 消息通知页面 component: './JobPortal/Message', diff --git a/src/pages/JobPortal/PersonalCenter/Interviews/index.tsx b/src/pages/JobPortal/PersonalCenter/Interviews/index.tsx new file mode 100644 index 0000000..500c3a1 --- /dev/null +++ b/src/pages/JobPortal/PersonalCenter/Interviews/index.tsx @@ -0,0 +1,179 @@ +import React, { useState, useEffect } from 'react'; +import { + Card, + Row, + Col, + Tag, + Space, + Typography, + Button, + Empty, + message, + Spin +} from 'antd'; +import { + EnvironmentOutlined, + ArrowLeftOutlined, + ClockCircleOutlined, + VideoCameraOutlined +} from '@ant-design/icons'; +import { history } from '@umijs/max'; +import JobPortalHeader from '@/components/JobPortalHeader'; +import { getCmsInterviewList } from '@/services/Management/list'; + +const { Title, Text } = Typography; + +// 面试状态映射 +const statusMap: Record = { + pending: { text: '待确认', color: 'blue' }, + accepted: { text: '已接受', color: 'green' }, + rejected: { text: '已拒绝', color: 'red' }, + completed: { text: '已完成', color: 'purple' }, +}; + +// 从缓存获取用户信息 +const getUserIdFromCache = (): number | null => { + try { + const cached = localStorage.getItem('userInfo'); + if (cached) { + const userInfo = JSON.parse(cached); + return userInfo?.userId || null; + } + } catch (error) { + console.error('读取缓存用户信息失败:', error); + } + return null; +}; + +const InterviewsPage: React.FC = () => { + const [interviews, setInterviews] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + fetchInterviews(); + }, []); + + const fetchInterviews = async () => { + const userId = getUserIdFromCache(); + if (!userId) { + message.warning('请先登录'); + history.push('/job-portal/personal-center'); + return; + } + + try { + setLoading(true); + const response = await getCmsInterviewList({ userId }); + if (response.code === 200) { + setInterviews(response.rows || []); + } else { + message.error(response.msg || '获取面试邀约列表失败'); + } + } catch (error) { + console.error('获取面试邀约列表失败:', error); + message.error('获取面试邀约列表失败'); + } finally { + setLoading(false); + } + }; + + const handleBack = () => { + history.push('/job-portal/personal-center'); + }; + + return ( +
+ + + +
+
+ +
+ +
+ 我的面试邀约 + 共 {interviews.length} 条记录 +
+ + {interviews.length === 0 && !loading ? ( + + ) : ( +
+ {interviews.map((item, index) => { + const st = statusMap[item.status || 'pending'] || statusMap.pending; + return ( + + + +
+ + <ClockCircleOutlined /> 面试时间:{item.interviewTime || '未指定'} + + + + {item.interviewMethod === 'online' ? '线上' : '线下'} + + {st.text} + +
+ {item.interviewMethod === 'online' ? ( + + + 腾讯会议 + + {item.meetingLink && ( + + 链接:{item.meetingLink} + + )} + {item.meetingPassword && ( + + 密码:{item.meetingPassword} + + )} + + ) : ( + + {item.interviewLocation || '未指定地点'} + + )} +
+ {item.contactPhone && ( +
+ 面试官联系方式:{item.contactPhone} +
+ )} + {item.remark && ( +
+ 备注:{item.remark} +
+ )} +
+ +
+
+ ); + })} +
+ )} +
+
+
+ ); +}; + +export default InterviewsPage; diff --git a/src/pages/JobPortal/PersonalCenter/index.tsx b/src/pages/JobPortal/PersonalCenter/index.tsx index d97daf8..41dcb3a 100644 --- a/src/pages/JobPortal/PersonalCenter/index.tsx +++ b/src/pages/JobPortal/PersonalCenter/index.tsx @@ -17,7 +17,8 @@ import { SafetyCertificateOutlined, BellOutlined, FileTextOutlined, - CameraOutlined + CameraOutlined, + CalendarOutlined } from '@ant-design/icons'; import { history } from '@umijs/max'; import JobPortalHeader from '@/components/JobPortalHeader'; @@ -179,6 +180,15 @@ const PersonalCenter: React.FC = () => { color: '#722ed1', link: '/job-portal/personal-center/showcase' }, + { + id: 6, + title: '我的面试邀约', + icon: , + status: '', + hasArrow: true, + color: '#1677ff', + link: '/job-portal/personal-center/interviews' + }, { id: 2, title: '在线面试', diff --git a/src/pages/Management/List/SeeMatching/index.tsx b/src/pages/Management/List/SeeMatching/index.tsx index 6b1c5d2..0a4f70b 100644 --- a/src/pages/Management/List/SeeMatching/index.tsx +++ b/src/pages/Management/List/SeeMatching/index.tsx @@ -298,6 +298,7 @@ function ManagementList() { { setResumeVisible(false); setCurrentRow(undefined); diff --git a/src/pages/Management/List/SeeMatching/resumeView.tsx b/src/pages/Management/List/SeeMatching/resumeView.tsx index d80bac6..e4293ae 100644 --- a/src/pages/Management/List/SeeMatching/resumeView.tsx +++ b/src/pages/Management/List/SeeMatching/resumeView.tsx @@ -1,9 +1,15 @@ -import {Modal, Card, Descriptions, List, Button} from 'antd'; +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 { - addCmsUserWorkExperiencesList + addCmsUserWorkExperiencesList, + addCmsInterview, } from '@/services/Management/list'; +import dayjs from 'dayjs'; + +const { TextArea } = Input; +const { Option } = Select; + export type ListFormProps = { onClose: (flag?: boolean, formVals?: unknown) => void; open: boolean; @@ -17,10 +23,18 @@ const listEdit: React.FC = (props) => { const [loading, setLoading] = useState(false); const [contactInfoList, setContactInfoList] = useState([]); + // 面试邀约相关状态 + const [interviewForm] = Form.useForm(); + const [interviewMethod, setInterviewMethod] = useState('online'); + const [submitting, setSubmitting] = useState(false); + useEffect(() => { if (props.open && props.values) { console.log(props.values); fetchResumeDetail(); + // 重置面试邀约表单 + interviewForm.resetFields(); + setInterviewMethod('online'); } }, [props.values,props.open]); @@ -41,6 +55,44 @@ const listEdit: React.FC = (props) => { } }; + // 发送面试邀约 + const handleInterviewSubmit = async () => { + try { + const values = await interviewForm.validateFields(); + setSubmitting(true); + + const payload: API.Management.InterviewInvitation = { + companyId: props.matching?.companyId, + jobId: Number(props.matching?.jobId), + userId: props.values?.userId, + applyId: props.values?.applyId ? Number(props.values.applyId) : undefined, + 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, + status: 'pending', + remark: values.remark, + }; + + const response = await addCmsInterview(payload); + if (response.code === 200) { + message.success('面试邀约已发送'); + interviewForm.resetFields(); + setInterviewMethod('online'); + } else { + message.error(response.msg || '发送失败'); + } + } catch (error) { + console.error('发送面试邀约失败:', error); + } finally { + setSubmitting(false); + } + }; + return ( = (props) => { /> } + + + +
+ + + + + + + + + {interviewMethod === 'online' && ( + <> + + + + + + + + )} + + {interviewMethod === 'offline' && ( + + + + )} + + +