feat: 优化岗位弹窗展示和添加投递权限限制

- 岗位描述和要求使用数字序号排序,左对齐展示
- 优化三个内容板块的间距和样式
- 添加岗位相关标签显示(专业相关/非专业相关/人才出海)
- 创建投递权限提示弹窗,非学员无法投递
- 更新岗位面试状态数据,修复重复的会展策划师记录
- 支持弹窗内容滚动,自适应高度

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KQL
2025-09-08 06:13:48 +08:00
parent 12e3722c54
commit 7132a9f190
9 changed files with 951 additions and 95 deletions

View File

@@ -1,9 +1,4 @@
[
{
"查询岗位名称": "会展策划师",
"阶段日期": "2025/9/16",
"面试状态": "收到Offer"
},
{
"查询岗位名称": "商业会展执行专员",
"阶段日期": "2025/7/30",
@@ -39,39 +34,24 @@
"阶段日期": "2025/7/23",
"面试状态": "收到Offer"
},
{
"查询岗位名称": "品牌推广专员",
"阶段日期": "2025/8/2",
"面试状态": "面试未通过"
},
{
"查询岗位名称": "会展策划师",
"阶段日期": "2025/9/16",
"面试状态": "收到Offer"
"面试状态": "等待HR通知"
},
{
"查询岗位名称": "客服",
"阶段日期": "2025/8/2",
"面试状态": "拒绝Offer"
},
{
"查询岗位名称": "境外展会操作助理",
"阶段日期": "2025/8/9",
"面试状态": "面试未通过"
"面试状态": "未参与面试"
},
{
"查询岗位名称": "海外活动策划专员",
"阶段日期": "2025/7/17",
"面试状态": "收到Offer"
"阶段日期": "2025/9/2",
"面试状态": "等待回复"
},
{
"查询岗位名称": "品牌公关",
"阶段日期": "2025/9/7",
"面试状态": "等待面试"
},
{
"查询岗位名称": "景区运营专员",
"阶段日期": "2025/8/12",
"面试状态": "面试未通过"
"面试状态": "等待回复"
}
]

View File

@@ -110,7 +110,8 @@ const transformCompanyJobs = (jobsData) => {
education: job["学历要求"],
positions: job["招聘人数"],
description: job["职位描述"],
requirements: job["任职要求"] ? job["任职要求"].split(/[;。\n]/).filter(r => r.trim()) : [],
requirements: job["任职要求"] ? job["任职要求"].split(/\d+\.\s*/).filter(r => r.trim()) : [],
requirementsText: job["任职要求"],
benefits: job["福利标签"] || [],
companyInfo: job["公司介绍"]
}

View File

@@ -1,5 +1,5 @@
.job-info-modal-content {
max-height: 720px;
max-height: 80vh;
width: 844px;
position: relative;
display: flex;
@@ -13,8 +13,28 @@
background-repeat: no-repeat;
border-radius: 8px;
box-sizing: border-box;
overflow: auto;
overflow-y: auto;
overflow-x: hidden;
padding: 20px;
/* 自定义滚动条样式 */
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: #667eea;
border-radius: 3px;
&:hover {
background: #764ba2;
}
}
.job-info-modal-search {
width: 319px;
@@ -35,13 +55,13 @@
}
.job-info-modal-user-resumes-list {
width: 100%;
min-height: 555px;
min-height: 400px;
margin-top: 16px;
display: grid;
grid-template-columns: repeat(2, 1fr); /* 每行两列 */
gap: 20px; /* 网格间距 */
justify-items: start; /* 项目左对 */
overflow-y: auto;
justify-items: start; /* 项目左对 */
overflow-y: visible;
.list-item {
width: 390px;
@@ -130,18 +150,32 @@
.job-info-modal-content-position-info {
width: 100%;
height: 30px;
min-height: 30px;
display: flex;
align-items: center;
justify-content: flex-start;
position: relative;
flex-wrap: wrap;
gap: 10px;
.job-info-modal-content-position-info-position {
font-size: 20px;
font-weight: 600;
line-height: 30px;
color: #1d2129;
margin-right: 10px;
}
.job-category-tag {
display: inline-flex;
align-items: center;
padding: 4px 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-size: 12px;
font-weight: 500;
border-radius: 12px;
white-space: nowrap;
box-shadow: 0 2px 4px rgba(102, 126, 234, 0.3);
}
.job-info-modal-content-position-info-num {
@@ -187,10 +221,25 @@
.job-info-modal-content-position-info-companyInfo {
width: 100%;
box-sizing: border-box;
padding: 16px;
border-radius: 8px;
background-color: #fff;
margin: 10px 0;
padding: 20px;
border-radius: 12px;
background: linear-gradient(135deg, #ffffff 0%, #f7f8fa 100%);
margin: 8px 0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(102, 126, 234, 0.1);
position: relative;
overflow: visible;
min-height: auto;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 4px;
height: 100%;
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
}
> p {
width: 100%;
@@ -199,28 +248,134 @@
.description-title,
.requirements-title,
.companyInfo-title {
font-size: 20px;
font-size: 16px;
font-weight: 600;
line-height: 30px;
color: #000;
margin-bottom: 10px;
line-height: 24px;
color: #1d2129;
margin-bottom: 16px;
padding-left: 12px;
position: relative;
&:before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 16px;
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
border-radius: 2px;
}
}
.description-content,
.description-content {
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: #4e5969;
text-align: left;
.description-item {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
text-align: left;
.description-number {
display: inline-block;
min-width: 24px;
font-size: 14px;
font-weight: 600;
color: #667eea;
margin-right: 8px;
text-align: left;
}
.description-text {
flex: 1;
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: #4e5969;
text-align: left;
}
&:last-child {
margin-bottom: 0;
}
}
}
.companyInfo-content {
font-size: 16px;
font-size: 14px;
font-weight: 400;
line-height: 24px;
color: #4e5969;
.company-paragraph {
margin-bottom: 12px;
text-indent: 2em;
text-align: justify;
&:last-child {
margin-bottom: 0;
}
}
}
.requirements-content {
width: 100%;
text-align: left;
.requirements-item {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
text-align: left;
font-size: 16px;
font-weight: 400;
line-height: 24px;
.requirement-number {
display: inline-block;
min-width: 24px;
font-size: 14px;
font-weight: 600;
color: #667eea;
margin-right: 8px;
text-align: left;
}
.requirement-text {
flex: 1;
font-size: 14px;
font-weight: 400;
line-height: 22px;
color: #4e5969;
text-align: left;
}
&:last-child {
margin-bottom: 0;
}
}
.requirement-line {
margin-bottom: 8px;
padding-left: 16px;
position: relative;
font-size: 14px;
line-height: 22px;
color: #4e5969;
text-align: left;
&:before {
content: "•";
position: absolute;
left: 0;
color: #667eea;
}
&:last-child {
margin-bottom: 0;
}
}
}
}

View File

@@ -6,6 +6,7 @@ import InfiniteScroll from "@/components/InfiniteScroll";
import toast from "@/components/Toast";
import FILEICON from "@/assets/images/CompanyJobsPage/file_icon.png";
import ResumeInfoModal from "../ResumeInfoModal";
import PermissionModal from "../PermissionModal";
import { getResumesList, submitResume, getPageData } from "@/services";
import "./index.css";
@@ -20,6 +21,7 @@ export default ({ visible, onClose, data, directToResume = false }) => {
const [resumeList, setResumeList] = useState([]); // 简历列表
const [listPage, setListPage] = useState(1);
const [listHasMore, setListHasMore] = useState(true);
const [permissionModalVisible, setPermissionModalVisible] = useState(false);
// 处理directToResume参数变化
useEffect(() => {
@@ -67,6 +69,11 @@ export default ({ visible, onClose, data, directToResume = false }) => {
// 选择简历投递
const userResumesClick = async (item) => {
// 显示权限提示弹窗
setPermissionModalVisible(true);
// 原投递逻辑暂时注释,实际使用时可根据用户权限判断
/*
try {
// 调用投递服务
const result = await submitResume({
@@ -102,6 +109,7 @@ export default ({ visible, onClose, data, directToResume = false }) => {
toast.error('投递失败,请重试');
console.error('投递失败:', error);
}
*/
};
// 点击简历详情
@@ -205,6 +213,12 @@ export default ({ visible, onClose, data, directToResume = false }) => {
<span className="job-info-modal-content-position-info-position">
{data?.position}
</span>
{/* 岗位相关标签 */}
{(data?.jobCategoryTag || data?.jobCategory) && (
<span className="job-category-tag">
{data?.jobCategoryTag || data?.jobCategory}
</span>
)}
<span className="job-info-modal-content-position-info-num">
该岗位仅剩{data?.remainingPositions}
</span>
@@ -224,39 +238,47 @@ export default ({ visible, onClose, data, directToResume = false }) => {
{data?.details?.description && (
<div className="job-info-modal-content-position-info-description">
<p className="description-title">岗位描述</p>
<p className="description-content">{data?.details?.description}</p>
<div className="description-content">
{data?.details?.description.split(/\d+\.\s*/).filter(item => item.trim()).map((item, index) => (
<div key={index} className="description-item">
<span className="description-number">{index + 1}.</span>
<span className="description-text">{item.trim()}</span>
</div>
))}
</div>
</div>
)}
{data?.details?.requirements?.length > 0 && (
{(data?.details?.requirements?.length > 0 || data?.details?.requirementsText) && (
<div className="job-info-modal-content-position-info-requirements">
<p className="requirements-title">岗位要求</p>
<ul className="requirements-content">
{data?.details?.requirements?.map((item, index) => (
<li key={index} className="requirements-item">
{index + 1}. {item}
</li>
))}
</ul>
<div className="requirements-content">
{data?.details?.requirements ? (
data?.details?.requirements?.map((item, index) => (
<div key={index} className="requirements-item">
<span className="requirement-number">{index + 1}.</span>
<span className="requirement-text">{item}</span>
</div>
))
) : (
data?.details?.requirementsText?.split('\n').map((line, index) => (
<p key={index} className="requirement-line">
{line}
</p>
))
)}
</div>
</div>
)}
{/* {data?.details?.requirements?.length > 0 && (
<div className="job-info-modal-content-position-info-requirements">
<p className="requirements-title">岗位要求</p>
<ul className="requirements-content">
{data?.details?.requirements?.map((item, index) => (
<li key={index} className="requirements-item">
{index + 1}.{item}
</li>
))}
</ul>
</div>
)} */}
{data?.details?.companyInfo && (
<div className="job-info-modal-content-position-info-companyInfo">
<p className="companyInfo-title">公司介绍</p>
<p className="companyInfo-content">
{data?.details?.companyInfo}
</p>
<div className="companyInfo-content">
{data?.details?.companyInfo.split('\n').map((paragraph, index) => (
<p key={index} className="company-paragraph">
{paragraph}
</p>
))}
</div>
</div>
)}
<div
@@ -278,6 +300,10 @@ export default ({ visible, onClose, data, directToResume = false }) => {
setResumeInfoData(null);
}}
/>
<PermissionModal
visible={permissionModalVisible}
onClose={() => setPermissionModalVisible(false)}
/>
</>
);
};

View File

@@ -0,0 +1,79 @@
.permission-modal-content {
width: 360px;
padding: 32px 30px;
display: flex;
flex-direction: column;
align-items: center;
background: linear-gradient(135deg, #ffffff 0%, #f7f8fa 100%);
border-radius: 12px;
position: relative;
overflow: hidden;
/* 装饰性背景 */
&::before {
content: "";
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(102, 126, 234, 0.05) 0%, transparent 70%);
animation: rotate 20s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
.permission-modal-title {
font-size: 20px;
font-weight: 600;
color: #1d2129;
margin: 0 0 20px;
position: relative;
z-index: 1;
text-align: center;
}
.permission-modal-message {
font-size: 16px;
font-weight: 500;
color: #4e5969;
margin: 0 0 28px;
text-align: center;
line-height: 24px;
position: relative;
z-index: 1;
}
.permission-modal-btn {
width: 120px;
height: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 20px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
z-index: 1;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
&:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
}

View File

@@ -0,0 +1,22 @@
import React from 'react';
import Modal from "@/components/Modal";
import './index.css';
export default ({ visible, onClose }) => {
return (
<Modal visible={visible} onClose={onClose}>
<div className="permission-modal-content">
<h2 className="permission-modal-title">权限提示</h2>
<p className="permission-modal-message">
非学员本人无岗位投递权限
</p>
<button className="permission-modal-btn" onClick={onClose}>
我知道了
</button>
</div>
</Modal>
);
};