简历详情完成

This commit is contained in:
bin
2025-11-07 11:19:09 +08:00
parent 3953717b60
commit 2c0bd8c617
4 changed files with 784 additions and 19 deletions

View File

@@ -114,7 +114,7 @@ function StorageDetectionList() {
> >
</Button> </Button>
<Button {/* <Button
type="link" type="link"
size="small" size="small"
key="edit" key="edit"
@@ -124,7 +124,7 @@ function StorageDetectionList() {
onClick={() => handleEdit(detectionId)} onClick={() => handleEdit(detectionId)}
> >
编辑 编辑
</Button> </Button> */}
</div> </div>
), ),
}, },

View File

@@ -1,4 +1,10 @@
import { ModalForm, ProDescriptions } from '@ant-design/pro-components';
import { Image, Card, Divider, Row, Col, Tag, Timeline, Empty } from 'antd';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { getDictValueEnum } from '@/services/system/dict';
import DictTag from '@/components/DictTag';
import mockData from './preView.json';
export type ResumeDetailProps = { export type ResumeDetailProps = {
onCancel: (flag?: boolean, formVals?: unknown) => void; onCancel: (flag?: boolean, formVals?: unknown) => void;
open: boolean; open: boolean;
@@ -6,7 +12,568 @@ export type ResumeDetailProps = {
}; };
const ResumeDetail: React.FC<ResumeDetailProps> = (props) => { const ResumeDetail: React.FC<ResumeDetailProps> = (props) => {
return <div></div>; const [sexEnum, setSexEnum] = useState<any>([]);
const [educationEnum, setEducationEnum] = useState<any>([]);
const [politicalEnum, setPoliticalEnum] = useState<any>([]);
const [areaEnum, setAreaEnum] = useState<any>([]);
const [experienceEnum, setExperienceEnum] = useState<any>([]);
const [ageEnum, setAgeEnum] = useState<any>([]);
// 获取字典数据
useEffect(() => {
getDictValueEnum('sys_user_sex', true).then((data) => {
setSexEnum(data);
});
getDictValueEnum('education', true, true).then((data) => {
setEducationEnum(data);
});
getDictValueEnum('political_affiliation', true, true).then((data) => {
setPoliticalEnum(data);
});
getDictValueEnum('area', true, true).then((data) => {
setAreaEnum(data);
});
getDictValueEnum('experience', true, true).then((data) => {
setExperienceEnum(data);
});
}, []);
const handleCancel = () => {
props.onCancel();
};
const { values } = props;
// const values = mockData;
// 格式化时间显示
const formatTimeRange = (start: string, end: string) => {
if (!start && !end) return '';
return `${start || ''} - ${end || '至今'}`;
};
// 渲染空状态
const renderEmpty = (description: string) => (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description={description}
style={{ margin: '20px 0' }}
/>
);
return (
<ModalForm
title="求职者简历详情"
open={props.open}
width={1000}
modalProps={{
destroyOnClose: true,
onCancel: () => handleCancel(),
footer: null,
width: 1200,
styles: {
body: { maxHeight: '80vh', overflowY: 'auto', padding: '16px', top: '50vh' },
},
}}
submitter={false}
>
{/* 基本信息卡片 */}
<Card
title="基本信息"
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
<Row gutter={16}>
<Col span={6}>
<div style={{ textAlign: 'center' }}>
{values?.avatar ? (
<Image
width={100}
height={100}
src={values.avatar}
style={{
borderRadius: '50%',
marginBottom: 12,
border: '2px solid #e8f4fd',
}}
preview={false}
/>
) : (
<div
style={{
width: 100,
height: 100,
borderRadius: '50%',
backgroundColor: '#f0f0f0',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto 12px',
fontSize: 12,
color: '#999',
border: '2px solid #e8f4fd',
}}
>
</div>
)}
<h3 style={{ margin: '8px 0 4px 0', color: '#1890ff' }}>
{values?.name || '未填写'}
</h3>
<p style={{ margin: 0, color: '#666', fontSize: 12 }}>
{values?.jobTitle?.join(' / ') || '暂无期望岗位'}
</p>
</div>
</Col>
<Col span={18}>
<ProDescriptions
column={3}
dataSource={values || {}}
size="small"
labelStyle={{ fontWeight: 'bold', color: '#333' }}
contentStyle={{ color: '#666' }}
>
{/* <ProDescriptions.Item dataIndex="userId" label="用户ID" /> */}
<ProDescriptions.Item
dataIndex="sex"
label="性别"
render={(text) => <DictTag enums={sexEnum} value={text as string} />}
/>
<ProDescriptions.Item dataIndex="age" label="年龄" />
<ProDescriptions.Item
dataIndex="education"
label="学历"
render={(text) => <DictTag enums={educationEnum} value={text as string} />}
/>
<ProDescriptions.Item
dataIndex="experience"
label="工作经验"
render={(text) => <DictTag enums={experienceEnum} value={text as string} />}
/>
<ProDescriptions.Item dataIndex="workYears" label="工作年限" />
<ProDescriptions.Item dataIndex="phone" label="手机号" />
<ProDescriptions.Item
dataIndex="politicalAffiliation"
label="政治面貌"
render={(text) => <DictTag enums={politicalEnum} value={text as string} />}
/>
<ProDescriptions.Item dataIndex="birthDate" label="出生日期" />
<ProDescriptions.Item dataIndex="idNumber" label="身份证号" />
<ProDescriptions.Item dataIndex="contactEmail" label="邮箱" />
<ProDescriptions.Item
dataIndex="area"
label="期望工作地"
render={(text) => <DictTag enums={areaEnum} value={text as string} />}
/>
<ProDescriptions.Item
dataIndex="salaryMin"
label="期望薪资"
render={(text, record) => (
<span style={{ color: '#ff4d4f', fontWeight: 'bold' }}>
{record.salaryMin || '面议'} - {record.salaryMax || '面议'} /
</span>
)}
/>
</ProDescriptions>
</Col>
</Row>
{/* 附加信息 */}
<Divider style={{ margin: '12px 0' }} />
<Row gutter={16}>
<Col span={12}>
<div>
<strong></strong>
{values?.graduationSchool || '未填写'}
</div>
</Col>
<Col span={12}>
<div>
<strong></strong>
{values?.major || '未填写'}
</div>
</Col>
<Col span={24} style={{ marginTop: 8 }}>
<div>
<strong></strong>
{values?.residenceAddress || '未填写'}
</div>
</Col>
</Row>
</Card>
{/* 个人介绍与求职意向 */}
<Card
title="个人介绍与求职意向"
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
<Row gutter={16}>
<Col span={24} style={{ marginBottom: 12 }}>
<div>
<strong style={{ color: '#1890ff' }}></strong>
<div
style={{
marginTop: 8,
color: '#666',
lineHeight: 1.6,
padding: 12,
backgroundColor: '#f8f9fa',
borderRadius: 4,
}}
>
{values?.introduction || '暂无个人介绍'}
</div>
</div>
</Col>
<Col span={24} style={{ marginBottom: 12 }}>
<div>
<strong style={{ color: '#1890ff' }}></strong>
<div
style={{
marginTop: 8,
color: '#666',
lineHeight: 1.6,
padding: 12,
backgroundColor: '#f8f9fa',
borderRadius: 4,
}}
>
{values?.jobIntention || '暂无求职意向'}
</div>
</div>
</Col>
<Col span={24}>
<div>
<strong style={{ color: '#1890ff' }}></strong>
<div
style={{
marginTop: 8,
color: '#666',
lineHeight: 1.6,
padding: 12,
backgroundColor: '#f8f9fa',
borderRadius: 4,
}}
>
{values?.selfEvaluation || '暂无自我评价'}
</div>
</div>
</Col>
</Row>
</Card>
{/* 教育经历 */}
<Card
title={`教育经历 (${values?.educationExp?.length || 0})`}
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
{values?.educationExp?.length > 0 ? (
<Timeline>
{values.educationExp.map((edu: any, index: number) => (
<Timeline.Item key={index} color="green">
<div style={{ padding: '8px 0' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
}}
>
<div>
<strong style={{ fontSize: 14, color: '#1890ff' }}>
{edu.school || '未填写学校'}
</strong>
<div style={{ color: '#666', marginTop: 4, display: 'flex' }}>
{edu.major || '未填写专业'} | {edu.degree || '未填写学位'} |
<div style={{ marginLeft: 5 }}>
<DictTag enums={educationEnum} value={edu.education} />
</div>{' '}
</div>
</div>
<div style={{ color: '#999', fontSize: 12, textAlign: 'right' }}>
{formatTimeRange(edu.startTime, edu.endTime)}
</div>
</div>
{(edu.degreeDate || edu.graduation) && (
<div style={{ color: '#999', fontSize: 12, marginTop: 4 }}>
{edu.degreeDate && `学位授予: ${edu.degreeDate}`}
{edu.graduation && ` 毕业日期: ${edu.graduation}`}
</div>
)}
</div>
</Timeline.Item>
))}
</Timeline>
) : (
renderEmpty('暂无教育经历')
)}
</Card>
{/* 工作经历 */}
<Card
title={`工作经历 (${values?.workExp?.length || 0})`}
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
{values?.workExp?.length > 0 ? (
<Timeline>
{values.workExp.map((work: any, index: number) => (
<Timeline.Item key={index} color="blue">
<div style={{ padding: '8px 0' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
}}
>
<div>
<strong style={{ fontSize: 14, color: '#1890ff' }}>
{work.company || '未填写公司'}
</strong>
<div style={{ color: '#666', marginTop: 4 }}>
{work.position || '未填写职位'} | {work.department || '未填写部门'}
{work.isFullTime !== null && (
<Tag
color={work.isFullTime ? 'blue' : 'orange'}
style={{ marginLeft: 8 }}
>
{work.isFullTime ? '全职' : '兼职'}
</Tag>
)}
</div>
</div>
<div style={{ color: '#999', fontSize: 12, textAlign: 'right' }}>
{formatTimeRange(work.startTime, work.endTime)}
</div>
</div>
{work.duty && (
<div
style={{
color: '#666',
marginTop: 8,
lineHeight: 1.6,
fontSize: 13,
padding: 8,
backgroundColor: '#f8f9fa',
borderRadius: 4,
}}
>
{work.duty}
</div>
)}
{work.salary && (
<div style={{ color: '#ff4d4f', fontSize: 12, marginTop: 4 }}>
: {work.salary}
</div>
)}
</div>
</Timeline.Item>
))}
</Timeline>
) : (
renderEmpty('暂无工作经历')
)}
</Card>
{/* 项目经历 */}
<Card
title={`项目经历 (${values?.projectExp?.length || 0})`}
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
{values?.projectExp?.length > 0 ? (
<Timeline>
{values.projectExp.map((project: any, index: number) => (
<Timeline.Item key={index} color="purple">
<div style={{ padding: '8px 0' }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'flex-start',
}}
>
<div>
<strong style={{ fontSize: 14, color: '#1890ff' }}>
{project.name || '未填写项目名称'}
</strong>
<div style={{ color: '#666', marginTop: 4 }}>
{project.role || '未填写角色'} | {project.position || '未填写岗位'}
</div>
</div>
<div style={{ color: '#999', fontSize: 12, textAlign: 'right' }}>
{formatTimeRange(project.startTime, project.endTime)}
</div>
</div>
{project.tasks && (
<div
style={{
color: '#666',
marginTop: 8,
lineHeight: 1.6,
fontSize: 13,
padding: 8,
backgroundColor: '#f8f9fa',
borderRadius: 4,
}}
>
{project.tasks}
</div>
)}
</div>
</Timeline.Item>
))}
</Timeline>
) : (
renderEmpty('暂无项目经历')
)}
</Card>
{/* 技能特长 */}
<Card
title={`技能特长 (${values?.skillList?.length || 0})`}
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
{values?.skillList?.length > 0 ? (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
{values.skillList.map((skill: any, index: number) => (
<Card.Grid
key={index}
style={{
width: 'calc(50% - 6px)',
boxShadow: 'none',
border: '1px solid #f0f0f0',
borderRadius: 6,
}}
>
<div>
<Tag color="blue" style={{ marginBottom: 8, fontSize: 12 }}>
{skill.skill}
</Tag>
{skill.proficiency && (
<div style={{ color: '#666', fontSize: 12, lineHeight: 1.5 }}>
{skill.proficiency}
</div>
)}
</div>
</Card.Grid>
))}
</div>
) : (
renderEmpty('暂无技能特长')
)}
</Card>
{/* 证书资质 */}
<Card
title={`证书资质 (${values?.certificateList?.length || 0})`}
style={{ marginBottom: 16 }}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
{values?.certificateList?.length > 0 ? (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
{values.certificateList.map((cert: any, index: number) => (
<Card.Grid
key={index}
style={{
width: 'calc(33.33% - 6px)',
boxShadow: 'none',
border: '1px solid #f0f0f0',
borderRadius: 6,
}}
>
<div>
<div style={{ fontWeight: 'bold', color: '#1890ff', fontSize: 13 }}>
{cert.name}
</div>
<div style={{ color: '#666', fontSize: 11, marginTop: 4 }}>
: {cert.grantUnit || '未填写'}
</div>
<div style={{ color: '#999', fontSize: 10, marginTop: 2 }}>
{cert.issueTime && `颁发时间: ${cert.issueTime}`}
{cert.managementPeriod && ` | 有效期: ${cert.managementPeriod}`}
</div>
</div>
</Card.Grid>
))}
</div>
) : (
renderEmpty('暂无证书资质')
)}
</Card>
{/* 专业技术职务 */}
<Card
title={`专业技术职务 (${values?.professionalTechnicalPostList?.length || 0})`}
size="small"
styles={{
header: { backgroundColor: '#fafafa' },
}}
>
{values?.professionalTechnicalPostList?.length > 0 ? (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
{values.professionalTechnicalPostList.map((post: any, index: number) => (
<Card.Grid
key={index}
style={{
width: 'calc(50% - 4px)',
boxShadow: 'none',
border: '1px solid #f0f0f0',
borderRadius: 6,
}}
>
<div>
<div style={{ fontWeight: 'bold', color: '#1890ff', fontSize: 13 }}>
{post.seriesLevelName} - {post.postSeriesName}
</div>
<div style={{ color: '#666', fontSize: 11, marginTop: 4 }}>
: {post.appointmentUnit || '未填写'}
</div>
<div style={{ color: '#999', fontSize: 10, marginTop: 2 }}>
{post.appointmentTime && `聘任时间: ${post.appointmentTime}`}
{post.fullTimeOrPartTime && ` | ${post.fullTimeOrPartTime}`}
</div>
{post.mainPost && (
<div style={{ color: '#666', fontSize: 11, marginTop: 4 }}>
: {post.mainPost}
</div>
)}
</div>
</Card.Grid>
))}
</div>
) : (
renderEmpty('暂无专业技术职务')
)}
</Card>
</ModalForm>
);
}; };
export default ResumeDetail; export default ResumeDetail;

View File

@@ -76,12 +76,6 @@ function ResumeList() {
valueType: 'text', valueType: 'text',
align: 'center', align: 'center',
}, },
{
title: '手机号',
dataIndex: 'phone',
valueType: 'text',
align: 'center',
},
{ {
title: '性别', title: '性别',
dataIndex: 'sex', dataIndex: 'sex',
@@ -99,6 +93,13 @@ function ResumeList() {
align: 'center', align: 'center',
hideInSearch: true, hideInSearch: true,
}, },
{
title: '手机号',
dataIndex: 'phone',
valueType: 'text',
align: 'center',
},
{ {
title: '出生日期', title: '出生日期',
dataIndex: 'birthDate', dataIndex: 'birthDate',
@@ -126,16 +127,6 @@ function ResumeList() {
return <DictTag enums={politicalEnum} value={record.politicalAffiliation} />; return <DictTag enums={politicalEnum} value={record.politicalAffiliation} />;
}, },
}, },
{
title: '期望薪资',
align: 'center',
hideInSearch: true,
render: (_, record) => (
<span>
{record.salaryMin} - {record.salaryMax}
</span>
),
},
{ {
title: '地区', title: '地区',
dataIndex: 'area', dataIndex: 'area',
@@ -146,6 +137,17 @@ function ResumeList() {
return <DictTag enums={areaEnum} value={record.area} />; return <DictTag enums={areaEnum} value={record.area} />;
}, },
}, },
{
title: '期望薪资',
align: 'center',
hideInSearch: true,
render: (_, record) => (
<span>
{record.salaryMin} - {record.salaryMax}
</span>
),
},
{ {
title: '状态', title: '状态',
dataIndex: 'status', dataIndex: 'status',

View File

@@ -0,0 +1,196 @@
{
"userId": 1001,
"name": "张明",
"age": "3",
"sex": "0",
"birthDate": "1992-05-15",
"education": "4",
"politicalAffiliation": "1",
"phone": "13800138000",
"avatar": "https://example.com/avatar/zhangming.jpg",
"salaryMin": "15000",
"salaryMax": "25000",
"area": "1",
"jobTitleId": "101,102,103",
"experience": "2",
"jobTitle": ["Java开发工程师", "后端开发工程师", "系统架构师"],
"idNumber": "110101199205150012",
"contactEmail": "zhangming@example.com",
"jobIntention": "寻求Java后端开发岗位希望在互联网行业发挥技术专长参与高并发分布式系统开发",
"graduationSchool": "清华大学",
"workYears": 5,
"residenceAddress": "北京市海淀区中关村大街1号",
"major": "计算机科学与技术",
"introduction": "5年Java开发经验精通Spring全家桶熟悉分布式系统架构设计有大型互联网项目开发经验",
"selfEvaluation": "技术扎实,学习能力强,具备良好的团队协作精神和问题解决能力,对新技术保持热情",
"educationExp": [
{
"startTime": "2010-09",
"endTime": "2014-06",
"school": "清华大学",
"major": "计算机科学与技术",
"education": "4",
"degree": "文学学士",
"degreeDate": "2014-06-30",
"graduation": "2014-06-30"
},
{
"startTime": "2014-09",
"endTime": "2017-06",
"school": "清华大学",
"major": "软件工程",
"education": "5",
"degree": "理学学士",
"degreeDate": "2017-06-30",
"graduation": "2017-06-30"
}
],
"workExp": [
{
"startTime": "2019-03",
"endTime": "至今",
"company": "阿里巴巴集团",
"department": "技术事业部",
"position": "高级Java开发工程师",
"duty": "负责电商平台核心交易系统开发,优化系统性能,参与分布式架构设计,主导技术方案评审",
"isFullTime": true,
"salary": "25k-35k"
},
{
"startTime": "2017-07",
"endTime": "2019-02",
"company": "腾讯科技有限公司",
"department": "微信支付事业部",
"position": "Java开发工程师",
"duty": "参与微信支付后台系统开发,负责交易模块功能实现,处理高并发场景下的技术挑战",
"isFullTime": true,
"salary": "18k-25k"
},
{
"startTime": "2016-03",
"endTime": "2017-06",
"company": "百度在线网络技术有限公司",
"department": "搜索事业部",
"position": "初级Java开发工程师",
"duty": "参与搜索引擎后端服务开发,学习大型系统架构设计,积累分布式系统开发经验",
"isFullTime": true,
"salary": "12k-18k"
}
],
"projectExp": [
{
"startTime": "2020-01",
"endTime": "2021-06",
"name": "电商平台分布式交易系统重构",
"role": "核心开发人员",
"position": "高级Java开发工程师",
"tasks": "负责交易核心模块重构引入分布式事务解决方案优化系统吞吐量提升300%处理峰值QPS达10万+"
},
{
"startTime": "2019-05",
"endTime": "2019-12",
"name": "微信支付风控系统升级",
"role": "开发负责人",
"position": "Java开发工程师",
"tasks": "主导风控规则引擎重构引入机器学习算法提升风险识别准确率至98%减少误判率50%"
},
{
"startTime": "2018-03",
"endTime": "2018-10",
"name": "智能推荐系统开发",
"role": "后端开发",
"position": "Java开发工程师",
"tasks": "参与推荐算法后端服务开发构建用户画像系统提升推荐点击率25%日均处理数据量1TB"
}
],
"certificateList": [
{
"name": "Oracle Certified Professional, Java SE 11 Developer",
"issueTime": "2020-08",
"grantUnit": "Oracle",
"level": "1",
"mainBasis": "通过Oracle官方认证考试",
"managementPeriod": "长期有效"
},
{
"name": "阿里云专业认证工程师",
"issueTime": "2019-12",
"grantUnit": "阿里云",
"level": "2",
"mainBasis": "通过阿里云专业技术认证",
"managementPeriod": "3年"
},
{
"name": "全国计算机等级考试四级",
"issueTime": "2013-09",
"grantUnit": "教育部考试中心",
"level": "2",
"mainBasis": "通过全国计算机等级考试",
"managementPeriod": "长期有效"
},
{
"name": "软件设计师",
"issueTime": "2016-05",
"grantUnit": "人力资源和社会保障部",
"level": "2",
"mainBasis": "通过全国计算机技术与软件专业技术资格考试",
"managementPeriod": "长期有效"
}
],
"skillList": [
{
"skill": "Java",
"proficiency": "精通8年经验熟悉JVM调优、多线程编程、性能优化"
},
{
"skill": "Spring Framework",
"proficiency": "精通6年项目经验熟悉Spring Boot、Spring Cloud、Spring MVC"
},
{
"skill": "MySQL",
"proficiency": "熟练5年经验熟悉数据库设计、SQL优化、索引调优"
},
{
"skill": "Redis",
"proficiency": "熟练4年经验熟悉缓存设计、集群部署、高可用方案"
},
{
"skill": "Docker",
"proficiency": "熟练3年经验熟悉容器化部署、镜像管理、编排工具"
},
{
"skill": "Kubernetes",
"proficiency": "掌握2年经验了解集群管理、服务发现、自动扩缩容"
},
{
"skill": "消息队列",
"proficiency": "熟练4年经验熟悉RabbitMQ、Kafka使用和原理"
},
{
"skill": "分布式系统",
"proficiency": "掌握3年经验了解分布式事务、服务治理、限流降级"
}
],
"professionalTechnicalPostList": [
{
"serialNumber": 1,
"postSeriesName": "计算机技术",
"appointmentTime": "2020-06-15",
"seriesLevelName": "高级工程师",
"appointmentUnit": "阿里巴巴集团",
"fullTimeOrPartTime": "专职",
"mainPost": "Java后端开发与系统架构",
"partTimeApprovalAuthority": ""
},
{
"serialNumber": 2,
"postSeriesName": "软件工程",
"appointmentTime": "2018-12-20",
"seriesLevelName": "工程师",
"appointmentUnit": "腾讯科技有限公司",
"fullTimeOrPartTime": "专职",
"mainPost": "后端系统开发",
"partTimeApprovalAuthority": ""
}
]
}