主要更新: - ✅ 完成主题配色从暗色到亮蓝白配色的全面转换 - ✅ 实现高薪岗位页面及后端API集成 - ✅ 完成登录注册页面及认证系统 - ✅ 实现预招录确认功能 - ✅ 添加数据库管理和维护工具脚本 - ✅ 优化错误处理和用户体验 核心功能: 1. 首页 (index.html) - 3D地球、专业分类、过渡岗位 2. 高薪岗位页面 (high.html) - 岗位详情、预招录确认、成功案例 3. 登录注册 (auth.html) - 用户认证、专业分类选择 4. 后端API - RESTful接口,JWT认证,MySQL数据库 技术栈: - 前端:Three.js, GSAP, 原生JavaScript - 后端:Node.js, Express, MySQL - 认证:JWT, bcrypt - 样式:自定义CSS,响应式设计 数据库工具: - kill-by-ids.js - 批量终止MySQL进程 - unlock-all-tables.js - 解锁数据库表 - init-db.js - 初始化数据库 - 其他管理脚本 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
842 lines
51 KiB
HTML
842 lines
51 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>岗位装配中心 - 详情增强版</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||
<style>
|
||
/* --- 核心视觉风格:深邃太空金属 --- */
|
||
:root {
|
||
--bg-color: #05070a;
|
||
--card-bg: #11141a;
|
||
--accent-cyan: #00f0ff;
|
||
--accent-blue: #2563eb;
|
||
--accent-green: #00ff9d;
|
||
--accent-orange: #ff9d00;
|
||
--text-main: #ffffff;
|
||
--text-sub: #94a3b8;
|
||
--border-color: rgba(255,255,255,0.08);
|
||
--modal-bg: rgba(15, 19, 26, 0.98);
|
||
}
|
||
|
||
body {
|
||
background-color: var(--bg-color);
|
||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
color: var(--text-main);
|
||
height: 100vh; overflow: hidden; position: relative;
|
||
}
|
||
|
||
/* 背景光影 */
|
||
body::before {
|
||
content: ''; position: fixed; top: -50%; left: -50%; width: 150%; height: 150%; z-index: -2;
|
||
background: radial-gradient(circle at center, rgba(0, 240, 255, 0.1) 0%, transparent 60%);
|
||
filter: blur(100px); opacity: 0.8; pointer-events: none;
|
||
}
|
||
body::after {
|
||
content: ''; position: fixed; bottom: -30%; right: -30%; width: 120%; height: 120%; z-index: -2;
|
||
background: radial-gradient(circle at center, rgba(25, 50, 150, 0.2) 0%, transparent 50%);
|
||
filter: blur(120px); opacity: 0.6; pointer-events: none;
|
||
}
|
||
|
||
::-webkit-scrollbar { width: 6px; }
|
||
::-webkit-scrollbar-track { background: transparent; }
|
||
::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
|
||
|
||
.commander-layout {
|
||
display: flex; flex-direction: column; height: 100vh;
|
||
max-width: 1440px; margin: 0 auto; padding: 20px 40px; box-sizing: border-box;
|
||
position: relative; z-index: 1;
|
||
}
|
||
|
||
/* --- 1. 顶部 HUD 分栏 --- */
|
||
.hud-split-wrapper {
|
||
display: flex; gap: 20px; margin-bottom: 25px; flex-shrink: 0;
|
||
}
|
||
|
||
.hud-panel {
|
||
background: rgba(17, 20, 26, 0.7); backdrop-filter: blur(20px);
|
||
border-radius: 16px; padding: 20px;
|
||
border: 1px solid var(--border-color);
|
||
box-shadow: 0 20px 40px -10px rgba(0,0,0,0.5);
|
||
display: flex; flex-direction: column;
|
||
}
|
||
|
||
.hud-left { flex: 3; }
|
||
.hud-right { flex: 1; border-color: rgba(255, 157, 0, 0.3); background: rgba(20, 15, 10, 0.7); }
|
||
|
||
.hud-header {
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
margin-bottom: 15px; border-bottom: 1px solid var(--border-color); padding-bottom: 10px;
|
||
}
|
||
|
||
.hud-title-group { display: flex; align-items: center; gap: 10px; }
|
||
.hud-icon { font-size: 1.2rem; text-shadow: 0 0 10px rgba(255,255,255,0.3); }
|
||
.hud-left .hud-icon { color: var(--accent-cyan); }
|
||
.hud-right .hud-icon { color: var(--accent-orange); }
|
||
.hud-title { font-size: 1rem; font-weight: bold; letter-spacing: 1px; color: #fff; }
|
||
.hud-subtitle { font-size: 0.7rem; color: #666; margin-left: 6px; font-weight: normal; }
|
||
|
||
.slots-container { display: grid; gap: 15px; flex: 1; }
|
||
.slots-left { grid-template-columns: repeat(3, 1fr); }
|
||
.slots-right { grid-template-columns: 1fr; }
|
||
|
||
.slot-module {
|
||
height: 120px; background: rgba(0,0,0,0.3); border: 1px dashed #333; border-radius: 8px;
|
||
display: flex; flex-direction: column; justify-content: center; align-items: center;
|
||
transition: all 0.3s; position: relative; overflow: hidden;
|
||
}
|
||
.slot-module.empty:hover { border-color: #555; background: rgba(255,255,255,0.02); }
|
||
|
||
.hud-left .slot-module.filled {
|
||
background: linear-gradient(160deg, #1e232e, #13161c); border: 1px solid var(--accent-cyan);
|
||
box-shadow: 0 0 15px rgba(0, 240, 255, 0.1); cursor: pointer;
|
||
}
|
||
.hud-right .slot-module.filled {
|
||
background: linear-gradient(160deg, #2e251e, #1c1613); border: 1px solid var(--accent-orange);
|
||
box-shadow: 0 0 15px rgba(255, 157, 0, 0.2); cursor: pointer;
|
||
}
|
||
.slot-module.filled:hover { transform: translateY(-3px); }
|
||
|
||
.slot-remove-mask {
|
||
position: absolute; inset: 0; background: rgba(0,0,0,0.85); display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||
opacity: 0; transition: opacity 0.2s; color: #ff4d4d;
|
||
}
|
||
.slot-module.filled:hover .slot-remove-mask { opacity: 1; }
|
||
|
||
.slot-content-title { font-size: 0.9rem; font-weight: bold; color: white; margin-bottom: 2px; text-align: center; width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.slot-content-salary { font-weight: bold; font-size: 0.85rem; font-family: 'Arial', sans-serif; }
|
||
.hud-left .slot-content-salary { color: var(--accent-cyan); }
|
||
.hud-right .slot-content-salary { color: var(--accent-orange); }
|
||
.slot-content-company { font-size: 0.7rem; color: var(--text-sub); }
|
||
.slot-index { position: absolute; top: 5px; left: 8px; font-size: 1.2rem; font-weight: 900; color: rgba(255,255,255,0.05); pointer-events: none; }
|
||
|
||
.action-btn {
|
||
width: 100%; margin-top: 15px; padding: 10px; border-radius: 6px;
|
||
font-weight: 800; font-size: 0.9rem; letter-spacing: 1px;
|
||
border: none; cursor: pointer; transition: all 0.3s;
|
||
display: flex; justify-content: center; align-items: center; gap: 8px;
|
||
opacity: 0.5; pointer-events: none; filter: grayscale(100%);
|
||
}
|
||
.action-btn.active { opacity: 1; pointer-events: auto; filter: grayscale(0%); }
|
||
.action-btn:hover { transform: scale(1.02); }
|
||
|
||
.btn-submit { background: linear-gradient(90deg, var(--accent-blue), var(--accent-cyan)); color: #000; box-shadow: 0 0 15px rgba(0, 240, 255, 0.2); }
|
||
.btn-submit.active:hover { box-shadow: 0 0 25px rgba(0, 240, 255, 0.5); color: white; }
|
||
.btn-confirm { background: linear-gradient(90deg, #d97706, var(--accent-orange)); color: #000; box-shadow: 0 0 15px rgba(255, 157, 0, 0.2); }
|
||
.btn-confirm.active:hover { box-shadow: 0 0 25px rgba(255, 157, 0, 0.5); color: white; }
|
||
|
||
/* --- 2. 列表与筛选 --- */
|
||
.list-section { flex: 1; overflow-y: auto; padding-right: 5px; }
|
||
.list-header {
|
||
display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px;
|
||
border-left: 4px solid var(--accent-cyan); padding-left: 12px;
|
||
}
|
||
.list-title { font-size: 1.1rem; font-weight: bold; color: #fff; letter-spacing: 1px; }
|
||
|
||
.filter-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; gap: 15px; position: relative; z-index: 40; }
|
||
.city-quick-select { display: flex; gap: 10px; align-items: center; }
|
||
.city-btn { padding: 6px 16px; border-radius: 4px; font-size: 0.85rem; color: var(--text-sub); background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.1); cursor: pointer; transition: all 0.3s; }
|
||
.city-btn:hover { background: rgba(255,255,255,0.08); color: #fff; }
|
||
.city-btn.active { background: rgba(0, 240, 255, 0.1); border-color: var(--accent-cyan); color: var(--accent-cyan); font-weight: bold; }
|
||
.more-city-btn { display: flex; align-items: center; gap: 6px; padding: 6px 16px; background: linear-gradient(145deg, #2a2e3a, #20232c); border: 1px solid var(--accent-cyan); color: var(--accent-cyan); border-radius: 4px; cursor: pointer; font-size: 0.85rem; font-weight: bold; transition: all 0.3s; }
|
||
.more-city-btn:hover { transform: translateY(-2px); box-shadow: 0 0 20px rgba(0,240,255,0.3); color: white; }
|
||
|
||
.type-selector { position: relative; min-width: 140px; }
|
||
.type-btn { width: 100%; background: linear-gradient(145deg, #1e232e, #13161c); border: 1px solid rgba(255,255,255,0.1); color: #fff; padding: 8px 15px; border-radius: 8px; font-size: 0.85rem; display: flex; justify-content: space-between; align-items: center; cursor: pointer; transition: all 0.3s; }
|
||
.type-btn:hover { border-color: var(--accent-blue); box-shadow: 0 0 15px rgba(37, 99, 235, 0.3); }
|
||
.type-dropdown { position: absolute; top: 110%; right: 0; width: 160px; z-index: 50; background: #151820; border: 1px solid var(--border-color); border-radius: 6px; box-shadow: 0 10px 30px rgba(0,0,0,0.8); padding: 5px; display: none; }
|
||
.type-dropdown.show { display: block; }
|
||
.type-option { padding: 8px 12px; font-size: 0.85rem; color: #999; cursor: pointer; border-radius: 4px; }
|
||
.type-option:hover { background: rgba(255,255,255,0.05); color: white; }
|
||
.type-option.selected { color: var(--accent-blue); background: rgba(37, 99, 235, 0.1); font-weight: bold; }
|
||
|
||
.job-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; padding-bottom: 40px; }
|
||
.job-card {
|
||
background: var(--card-bg); border: 1px solid var(--border-color); border-radius: 12px;
|
||
padding: 24px; position: relative; cursor: pointer; transition: all 0.25s ease-out;
|
||
display: flex; flex-direction: column; justify-content: space-between; min-height: 190px; /* 增加高度以容纳按钮 */
|
||
}
|
||
.job-card:hover { border-color: rgba(0, 240, 255, 0.5); background: rgba(26, 30, 38, 0.8); transform: translateY(-5px) scale(1.01); box-shadow: 0 15px 35px rgba(0,0,0,0.4), 0 0 15px rgba(0,240,255,0.1); }
|
||
.job-card.disabled { opacity: 0.4; filter: grayscale(100%); background: #0a0c10; border-color: #333; pointer-events: none; cursor: default; transform: none !important; box-shadow: none !important; }
|
||
|
||
.card-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
|
||
.card-icon { width: 44px; height: 44px; border-radius: 8px; background: rgba(255,255,255,0.03); display: flex; align-items: center; justify-content: center; font-size: 1.2rem; color: var(--text-sub); transition: color 0.3s; }
|
||
.job-card:hover .card-icon { color: var(--accent-cyan); background: rgba(0, 240, 255, 0.05); }
|
||
.card-salary { color: var(--accent-cyan); font-weight: bold; font-size: 1rem; }
|
||
.card-title { font-size: 1.1rem; font-weight: bold; color: #fff; margin-bottom: 6px; }
|
||
.card-company { font-size: 0.85rem; color: var(--text-sub); display: flex; align-items: center; gap: 6px; }
|
||
|
||
.card-tags { margin-top: 12px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
|
||
.tag { font-size: 0.75rem; padding: 3px 8px; border-radius: 4px; background: rgba(255,255,255,0.03); color: #888; border: 1px solid rgba(255,255,255,0.02); }
|
||
|
||
.type-badge {
|
||
display: inline-block; font-size: 0.75rem; padding: 3px 8px; border-radius: 4px;
|
||
margin-right: 6px; font-weight: bold; letter-spacing: 0.5px;
|
||
}
|
||
.type-produce { background: rgba(37, 99, 235, 0.15); color: #60a5fa; border: 1px solid rgba(37, 99, 235, 0.3); }
|
||
.type-service { background: rgba(0, 255, 157, 0.1); color: #34d399; border: 1px solid rgba(0, 255, 157, 0.3); }
|
||
|
||
.check-mark { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.6); color: var(--accent-cyan); font-size: 2rem; opacity: 0; transition: all 0.3s; pointer-events: none; z-index: 10; }
|
||
.job-card.disabled .check-mark { opacity: 1; }
|
||
|
||
/* 名额角标 */
|
||
.quota-badge {
|
||
position: absolute; top: 0; right: 0;
|
||
background: rgba(255, 157, 0, 0.15); border-bottom-left-radius: 12px;
|
||
padding: 4px 10px; border-left: 1px solid rgba(255, 157, 0, 0.3); border-bottom: 1px solid rgba(255, 157, 0, 0.3);
|
||
font-size: 0.75rem; color: var(--accent-orange); font-weight: bold; letter-spacing: 0.5px;
|
||
}
|
||
|
||
/* --- 新增:详情按钮 --- */
|
||
.btn-detail {
|
||
width: 100%;
|
||
padding: 8px 0;
|
||
border: 1px solid rgba(255,255,255,0.1);
|
||
background: rgba(255,255,255,0.03);
|
||
color: var(--text-sub);
|
||
font-size: 0.8rem;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
margin-top: auto; /* 推到底部 */
|
||
display: flex; align-items: center; justify-content: center; gap: 6px;
|
||
}
|
||
.btn-detail:hover {
|
||
border-color: var(--accent-cyan);
|
||
color: var(--accent-cyan);
|
||
background: rgba(0, 240, 255, 0.05);
|
||
}
|
||
|
||
/* --- 模态框通用 --- */
|
||
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.85); backdrop-filter: blur(8px); z-index: 100; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.3s; }
|
||
.modal-overlay.show { opacity: 1; pointer-events: auto; }
|
||
|
||
/* 城市选择模态框 */
|
||
.city-modal { width: 900px; max-width: 90vw; height: 600px; background: #1a1e26; border: 1px solid var(--border-color); border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,0.8); display: flex; flex-direction: column; }
|
||
|
||
/* --- 新增:岗位详情模态框 --- */
|
||
.detail-modal {
|
||
width: 1000px; max-width: 95vw; height: 80vh;
|
||
background: #11141a; border: 1px solid var(--border-color); border-radius: 16px;
|
||
box-shadow: 0 0 50px rgba(0,0,0,0.9), 0 0 0 1px rgba(0, 240, 255, 0.1);
|
||
display: flex; flex-direction: column; overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.detail-header {
|
||
padding: 24px 30px;
|
||
background: linear-gradient(90deg, rgba(0, 240, 255, 0.05), transparent);
|
||
border-bottom: 1px solid var(--border-color);
|
||
display: flex; justify-content: space-between; align-items: flex-start;
|
||
}
|
||
.detail-title-area { flex: 1; }
|
||
.detail-job-title { font-size: 1.8rem; font-weight: bold; color: white; margin-bottom: 8px; display: flex; align-items: center; gap: 15px; }
|
||
.detail-salary-tag { font-size: 1.2rem; color: var(--accent-cyan); font-family: 'Arial', sans-serif; background: rgba(0, 240, 255, 0.1); padding: 2px 12px; border-radius: 4px; border: 1px solid rgba(0, 240, 255, 0.3); }
|
||
.detail-company-name { font-size: 1rem; color: var(--text-sub); display: flex; align-items: center; gap: 8px; }
|
||
|
||
.detail-body { display: flex; flex: 1; overflow: hidden; }
|
||
|
||
/* 左侧:核心参数栏 */
|
||
.detail-sidebar {
|
||
width: 320px; background: rgba(0,0,0,0.2); border-right: 1px solid var(--border-color);
|
||
padding: 25px; overflow-y: auto; display: flex; flex-direction: column; gap: 20px;
|
||
}
|
||
.param-group { background: rgba(255,255,255,0.03); padding: 15px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.05); }
|
||
.param-label { color: #666; font-size: 0.8rem; margin-bottom: 5px; text-transform: uppercase; letter-spacing: 1px; }
|
||
.param-value { color: white; font-weight: bold; font-size: 0.95rem; display: flex; align-items: center; gap: 8px; }
|
||
.param-icon { width: 24px; text-align: center; color: var(--accent-blue); }
|
||
|
||
/* 右侧:详细描述 */
|
||
.detail-content { flex: 1; padding: 30px; overflow-y: auto; }
|
||
.section-title { font-size: 1.1rem; font-weight: bold; color: white; margin-bottom: 15px; padding-left: 10px; border-left: 3px solid var(--accent-cyan); display: flex; align-items: center; gap: 10px; }
|
||
.req-list { list-style: none; padding: 0; margin-bottom: 30px; }
|
||
.req-item { position: relative; padding-left: 20px; margin-bottom: 10px; color: #ccc; line-height: 1.6; font-size: 0.95rem; }
|
||
.req-item::before { content: '▹'; position: absolute; left: 0; color: var(--accent-cyan); }
|
||
|
||
.welfare-grid { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 30px; }
|
||
.welfare-tag { background: rgba(37, 99, 235, 0.1); border: 1px solid rgba(37, 99, 235, 0.3); color: #93c5fd; padding: 6px 12px; border-radius: 20px; font-size: 0.85rem; }
|
||
|
||
.company-intro { color: #999; line-height: 1.7; font-size: 0.9rem; text-align: justify; }
|
||
|
||
.modal-close-btn {
|
||
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
|
||
border-radius: 50%; background: rgba(255,255,255,0.1); color: white; cursor: pointer; transition: all 0.2s;
|
||
}
|
||
.modal-close-btn:hover { background: #ff4d4d; transform: rotate(90deg); }
|
||
|
||
/* Modal 内部滚动条 */
|
||
.detail-sidebar::-webkit-scrollbar, .detail-content::-webkit-scrollbar { width: 5px; }
|
||
.detail-sidebar::-webkit-scrollbar-thumb, .detail-content::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
|
||
|
||
/* Modal 通用样式补丁 */
|
||
.modal-header { padding: 15px 25px; border-bottom: 1px solid #333; display: flex; justify-content: space-between; align-items: center; background: #151820; }
|
||
.modal-title { font-size: 1.1rem; font-weight: bold; color: white; letter-spacing: 1px; }
|
||
.modal-body { display: flex; flex: 1; overflow: hidden; }
|
||
.region-sidebar { width: 160px; background: #111; border-right: 1px solid #333; overflow-y: auto; }
|
||
.region-item { padding: 15px 20px; color: #888; cursor: pointer; border-left: 3px solid transparent; transition: all 0.2s; }
|
||
.region-item:hover { color: white; background: #222; }
|
||
.region-item.active { background: #222; color: var(--accent-cyan); border-left-color: var(--accent-cyan); font-weight: bold; }
|
||
.city-grid-area { flex: 1; padding: 25px; overflow-y: auto; background: #1a1e26; }
|
||
.city-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 12px; }
|
||
.city-option { padding: 8px; text-align: center; border-radius: 4px; background: #252a33; cursor: pointer; color: #ccc; font-size: 0.9rem; transition: all 0.2s; }
|
||
.city-option:hover { background: #333; color: white; transform: translateY(-2px); border: 1px solid var(--accent-cyan); }
|
||
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="commander-layout">
|
||
|
||
<div class="hud-split-wrapper">
|
||
|
||
<div class="hud-panel hud-left">
|
||
<div class="hud-header">
|
||
<div class="hud-title-group">
|
||
<i class="fa-solid fa-cube hud-icon"></i>
|
||
<div>
|
||
<div class="hud-title">我的意向过渡岗位</div>
|
||
<span class="hud-subtitle">INTENDED POSITIONS</span>
|
||
</div>
|
||
</div>
|
||
<div class="text-gray-500 font-mono text-sm">
|
||
已选: <span id="count-left" class="text-cyan-400 font-bold text-lg">0</span> / 3
|
||
</div>
|
||
</div>
|
||
<div class="slots-container slots-left" id="slots-left"></div>
|
||
<button id="btn-submit-intent" class="action-btn btn-submit">
|
||
提交意向清单 <i class="fa-solid fa-cloud-arrow-up"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="hud-panel hud-right">
|
||
<div class="hud-header">
|
||
<div class="hud-title-group">
|
||
<i class="fa-solid fa-star hud-icon"></i>
|
||
<div>
|
||
<div class="hud-title">过渡岗位确认</div>
|
||
<span class="hud-subtitle">FINAL LOCK</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="slots-container slots-right" id="slots-right"></div>
|
||
<button id="btn-confirm-final" class="action-btn btn-confirm">
|
||
确认锁定 <i class="fa-solid fa-lock"></i>
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<section class="list-section custom-scrollbar">
|
||
<div class="list-header">
|
||
<div class="list-title">岗位资源库</div>
|
||
</div>
|
||
|
||
<div class="filter-bar">
|
||
<div class="city-quick-select" id="city-quick-select"></div>
|
||
<div class="type-selector" id="type-selector">
|
||
<button class="type-btn" onclick="toggleTypeDropdown()">
|
||
<span id="current-type">全部类型</span>
|
||
<i class="fa-solid fa-chevron-down text-xs text-gray-500"></i>
|
||
</button>
|
||
<div class="type-dropdown" id="type-dropdown"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="job-grid" id="job-grid"></div>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
<div class="modal-overlay" id="city-modal-overlay">
|
||
<div class="city-modal">
|
||
<div class="modal-header">
|
||
<div class="modal-title">选择城市</div>
|
||
<div class="modal-close-btn" onclick="closeCityModal()"><i class="fa-solid fa-xmark"></i></div>
|
||
</div>
|
||
<div class="modal-body">
|
||
<ul class="region-sidebar" id="region-sidebar"></ul>
|
||
<div class="city-grid-area">
|
||
<div class="city-grid" id="city-grid"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-overlay" id="detail-modal-overlay">
|
||
<div class="detail-modal">
|
||
<div class="detail-header">
|
||
<div class="detail-title-area">
|
||
<div class="detail-job-title">
|
||
<span id="detail-title">岗位名称</span>
|
||
<span id="detail-salary" class="detail-salary-tag">4K-6K</span>
|
||
</div>
|
||
<div class="detail-company-name">
|
||
<i class="fa-regular fa-building"></i> <span id="detail-company">公司名称</span>
|
||
<span class="text-gray-600 mx-2">|</span>
|
||
<span id="detail-type" class="text-blue-400">岗位类型</span>
|
||
</div>
|
||
</div>
|
||
<div class="modal-close-btn" onclick="closeDetailModal()"><i class="fa-solid fa-xmark"></i></div>
|
||
</div>
|
||
<div class="detail-body">
|
||
<div class="detail-sidebar">
|
||
<div class="param-group">
|
||
<div class="param-label">到岗时间</div>
|
||
<div class="param-value"><i class="fa-regular fa-calendar param-icon"></i> <span id="detail-start-date">--</span></div>
|
||
</div>
|
||
<div class="param-group">
|
||
<div class="param-label">工作周期</div>
|
||
<div class="param-value"><i class="fa-solid fa-rotate param-icon"></i> <span id="detail-duration">--</span></div>
|
||
</div>
|
||
<div class="param-group">
|
||
<div class="param-label">报道地点</div>
|
||
<div class="param-value"><i class="fa-solid fa-location-dot param-icon"></i> <span id="detail-location">--</span></div>
|
||
</div>
|
||
<div class="param-group">
|
||
<div class="param-label">吃住情况</div>
|
||
<div class="param-value"><i class="fa-solid fa-utensils param-icon"></i> <span id="detail-roomboard">--</span></div>
|
||
</div>
|
||
<div class="param-group">
|
||
<div class="param-label">剩余名额</div>
|
||
<div class="param-value text-orange-500"><i class="fa-solid fa-users param-icon text-orange-500"></i> <span id="detail-quota">--</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="detail-content">
|
||
<div class="section-title"><i class="fa-solid fa-list-check"></i> 岗位要求</div>
|
||
<ul class="req-list" id="detail-requirements">
|
||
</ul>
|
||
|
||
<div class="section-title"><i class="fa-solid fa-gift"></i> 福利待遇</div>
|
||
<div class="welfare-grid" id="detail-benefits">
|
||
</div>
|
||
|
||
<div class="section-title"><i class="fa-regular fa-building"></i> 企业介绍</div>
|
||
<div class="company-intro" id="detail-company-intro">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||
|
||
<script>
|
||
// --- 1. 全量城市数据 ---
|
||
const regionData = {
|
||
"热门城市": ["全部", "北京市", "上海市", "广州市", "深圳市", "成都市", "杭州市", "武汉市", "苏州市", "南京市", "重庆市", "天津市", "西安市"],
|
||
"直辖市": ["北京市", "上海市", "天津市", "重庆市"],
|
||
"广东省": ["广州市", "深圳市", "珠海市", "汕头市", "佛山市", "韶关市", "湛江市", "肇庆市", "江门市", "茂名市", "惠州市", "梅州市", "汕尾市", "河源市", "阳江市", "清远市", "东莞市", "中山市", "潮州市", "揭阳市", "云浮市"],
|
||
"江苏省": ["南京市", "无锡市", "徐州市", "常州市", "苏州市", "南通市", "连云港市", "淮安市", "盐城市", "扬州市", "镇江市", "泰州市", "宿迁市"],
|
||
"浙江省": ["杭州市", "宁波市", "温州市", "嘉兴市", "湖州市", "绍兴市", "金华市", "衢州市", "舟山市", "台州市", "丽水市"],
|
||
"山东省": ["济南市", "青岛市", "淄博市", "枣庄市", "东营市", "烟台市", "潍坊市", "济宁市", "泰安市", "威海市", "日照市", "临沂市", "德州市", "聊城市", "滨州市", "菏泽市"],
|
||
"四川省": ["成都市", "自贡市", "攀枝花市", "泸州市", "德阳市", "绵阳市", "广元市", "遂宁市", "内江市", "乐山市", "南充市", "眉山市", "宜宾市", "广安市", "达州市", "雅安市", "巴中市", "资阳市"],
|
||
"河南省": ["郑州市", "开封市", "洛阳市", "平顶山市", "安阳市", "鹤壁市", "新乡市", "焦作市", "濮阳市", "许昌市", "漯河市", "三门峡市", "南阳市", "商丘市", "信阳市", "周口市", "驻马店市"],
|
||
"湖北省": ["武汉市", "黄石市", "十堰市", "宜昌市", "襄阳市", "鄂州市", "荆门市", "孝感市", "荆州市", "黄冈市", "咸宁市", "随州市", "恩施州"],
|
||
"湖南省": ["长沙市", "株洲市", "湘潭市", "衡阳市", "邵阳市", "岳阳市", "常德市", "张家界市", "益阳市", "郴州市", "永州市", "怀化市", "娄底市"],
|
||
"福建省": ["福州市", "厦门市", "莆田市", "三明市", "泉州市", "漳州市", "南平市", "龙岩市", "宁德市"],
|
||
"安徽省": ["合肥市", "芜湖市", "蚌埠市", "淮南市", "马鞍山市", "淮北市", "铜陵市", "安庆市", "黄山市", "滁州市", "阜阳市", "宿州市", "六安市", "亳州市", "池州市", "宣城市"],
|
||
"河北省": ["石家庄市", "唐山市", "秦皇岛市", "邯郸市", "邢台市", "保定市", "张家口市", "承德市", "沧州市", "廊坊市", "衡水市"],
|
||
"陕西省": ["西安市", "铜川市", "宝鸡市", "咸阳市", "渭南市", "延安市", "汉中市", "榆林市", "安康市", "商洛市"],
|
||
"辽宁省": ["沈阳市", "大连市", "鞍山市", "抚顺市", "本溪市", "丹东市", "锦州市", "营口市", "阜新市", "辽阳市", "盘锦市", "铁岭市", "朝阳市", "葫芦岛市"]
|
||
};
|
||
|
||
// --- 2. 岗位数据 (已扩展详情字段) ---
|
||
// 使用 CSV 中的第一条真实数据演示,其他为模拟数据
|
||
const allJobs = [
|
||
{
|
||
id: 1,
|
||
title: "自动化技术员",
|
||
company: "恒力集团",
|
||
salary: "4K-6K",
|
||
icon: "fa-microchip",
|
||
tags: ["苏州", "包吃住"],
|
||
type: "生产类岗位",
|
||
city: "苏州市",
|
||
quota: 150,
|
||
occupied: 45,
|
||
// --- 详情字段 ---
|
||
startDate: "2026/3/5",
|
||
reportLocation: "江苏省苏州市吴江区盛泽镇南麻恒力路1号",
|
||
duration: "3个月",
|
||
roomBoard: "包吃包住",
|
||
requirements: "电气、自动化或相关专业,大专及以上学历;\n熟悉 PLC/继电器/电气控制原理,能独立完成设备电气线路安装与调试;\n有自动化设备维护、故障分析经验者优先;\n工作认真负责,有团队协作精神。",
|
||
benefits: ["五险一金", "年终奖", "项目奖金", "单休", "员工宿舍", "绩效奖金"],
|
||
companyIntro: "恒力集团有限公司成立于1994年,总部位于江苏省苏州市吴江区,是立足实业发展的全球化民营企业。集团注册资产达520亿元人民币,全球员工总数超12万人,2022年实现营业收入6117亿元,连续七年跻身世界500强企业榜单。公司以石化、聚酯新材料、纺织为主业,构建形成“原油-芳烃-乙烯-精对苯二甲酸(PTA)-聚酯-纺织”全产业链布局。"
|
||
},
|
||
{
|
||
id: 2, title: "跨境电商运营总监", company: "SHEIN", salary: "40-70k", icon: "fa-globe", tags: ["广州", "欧美"], type: "服务类岗位", city: "广州市", quota: 2, occupied: 1,
|
||
startDate: "2025/12/1", reportLocation: "广州市番禺区万博商务区", duration: "长期", roomBoard: "提供餐补", requirements: "5年以上跨境电商运营经验;\n英语流利,可作为工作语言;\n具备优秀的团队管理能力。", benefits: ["股票期权", "带薪年假", "年度旅游"], companyIntro: "SHEIN是一家全球领先的时尚和生活方式在线零售商,致力于让“人人尽享时尚之美”。"
|
||
},
|
||
{
|
||
id: 3, title: "AI 大模型算法专家", company: "未来智能", salary: "50-80k", icon: "fa-brain", tags: ["北京", "NLP"], type: "生产类岗位", city: "北京市", quota: 3, occupied: 3,
|
||
startDate: "2025/11/15", reportLocation: "北京市海淀区中关村软件园", duration: "长期", roomBoard: "不包吃住", requirements: "计算机相关专业博士学历;\n在NLP领域有顶级会议论文发表;\n熟悉PyTorch/TensorFlow。", benefits: ["补充医疗保险", "弹性工作", "定期体检"], companyIntro: "未来智能致力于构建通用人工智能,为人类社会带来变革性的技术进步。"
|
||
},
|
||
{
|
||
id: 4, title: "资深产品经理", company: "字节跳动", salary: "30-55k", icon: "fa-cube", tags: ["上海", "SaaS"], type: "生产类岗位", city: "上海市", quota: 4, occupied: 2,
|
||
startDate: "尽快", reportLocation: "上海市闵行区宜山路", duration: "长期", roomBoard: "免费三餐", requirements: "3年以上B端产品经验;\n逻辑思维清晰,具备优秀的数据分析能力。", benefits: ["房补", "免费健身房", "下午茶"], companyIntro: "字节跳动是最早将人工智能应用于移动互联网场景的科技企业之一。"
|
||
},
|
||
{
|
||
id: 5, title: "供应链数据分析师", company: "恒力集团", salary: "18-30k", icon: "fa-chart-pie", tags: ["大连", "SQL"], type: "服务类岗位", city: "大连市", quota: 8, occupied: 5,
|
||
startDate: "2026/1/10", reportLocation: "大连市长兴岛经济区", duration: "1年", roomBoard: "提供宿舍", requirements: "统计学或数学相关专业;\n精通SQL/Python;\n对数字敏感。", benefits: ["五险一金", "班车接送"], companyIntro: "恒力石化(大连)有限公司是恒力集团在东北地区的重要产业基地。"
|
||
},
|
||
{ id: 6, title: "嵌入式系统架构师", company: "科沃斯", salary: "35-60k", icon: "fa-microchip", tags: ["苏州", "ROS"], type: "生产类岗位", city: "苏州市", quota: 1, occupied: 0, startDate: "2025/12/20", reportLocation: "苏州市吴中区石湖西路", duration: "长期", roomBoard: "餐补", requirements: "熟悉嵌入式Linux开发;\n精通C/C++。", benefits: ["年终奖", "股票期权"], companyIntro: "科沃斯机器人是全球最早的服务机器人研发与生产商之一。" },
|
||
{ id: 7, title: "HRBP (研发向)", company: "阿里巴巴", salary: "25-40k", icon: "fa-users", tags: ["杭州", "OD"], type: "服务类岗位", city: "杭州市", quota: 2, occupied: 1, startDate: "2025/11/30", reportLocation: "杭州市余杭区文一西路", duration: "长期", roomBoard: "餐补", requirements: "5年以上HR经验;\n熟悉互联网研发团队特点。", benefits: ["主要看阿里福报"], companyIntro: "阿里巴巴集团旨在构建未来的商业基础设施。" },
|
||
{ id: 8, title: "高级前端工程师", company: "微软", salary: "30-50k", icon: "fa-code", tags: ["苏州", "React"], type: "生产类岗位", city: "苏州市", quota: 6, occupied: 4, startDate: "2026/2/1", reportLocation: "苏州市工业园区星湖街", duration: "长期", roomBoard: "不包", requirements: "精通React/Vue;\n英语读写流利。", benefits: ["15天年假", "补充医疗"], companyIntro: "微软(中国)有限公司是微软公司在中国设立的子公司。" },
|
||
{ id: 9, title: "品牌视觉设计专家", company: "腾讯", salary: "25-45k", icon: "fa-pen-nib", tags: ["深圳", "C4D"], type: "生产类岗位", city: "深圳市", quota: 3, occupied: 2, startDate: "2025/12/15", reportLocation: "深圳市南山区深南大道", duration: "长期", roomBoard: "班车", requirements: "美术类专业;\n精通C4D/PS/AI。", benefits: ["Q币", "公仔"], companyIntro: "腾讯是一家世界领先的互联网科技公司。" },
|
||
{ id: 10, title: "财务主管", company: "美团", salary: "20-35k", icon: "fa-calculator", tags: ["北京", "CPA"], type: "服务类岗位", city: "北京市", quota: 2, occupied: 0, startDate: "2026/1/5", reportLocation: "北京市朝阳区望京东路", duration: "长期", roomBoard: "餐补", requirements: "持有CPA证书;\n3年以上财务主管经验。", benefits: ["六险一金"], companyIntro: "美团的使命是“帮大家吃得更好,生活更好”。" },
|
||
{ id: 11, title: "新媒体运营", company: "小红书", salary: "15-25k", icon: "fa-hashtag", tags: ["上海", "营销"], type: "服务类岗位", city: "上海市", quota: 10, occupied: 8, startDate: "2025/11/25", reportLocation: "上海市黄浦区马当路", duration: "长期", roomBoard: "下午茶", requirements: "网感好;\n熟悉小红书平台规则。", benefits: ["宠物友好"], companyIntro: "小红书是年轻人的生活方式平台。" },
|
||
{ id: 12, title: "游戏客户端开发", company: "米哈游", salary: "30-60k", icon: "fa-gamepad", tags: ["上海", "Unity"], type: "生产类岗位", city: "上海市", quota: 5, occupied: 1, startDate: "2025/12/10", reportLocation: "上海市徐汇区宜山路", duration: "长期", roomBoard: "免费晚餐", requirements: "热爱二次元;\n精通Unity3D。", benefits: ["免费咖啡", "年会大奖"], companyIntro: "技术宅拯救世界。" }
|
||
];
|
||
|
||
// --- 状态管理 ---
|
||
let selectedJobs = [];
|
||
let filterState = { city: "全部", type: "全部" };
|
||
let currentRegion = "热门城市";
|
||
const MAX_SELECTION = 4;
|
||
|
||
// --- DOM ---
|
||
const slotsLeft = document.getElementById('slots-left');
|
||
const slotsRight = document.getElementById('slots-right');
|
||
const jobGrid = document.getElementById('job-grid');
|
||
const countLeft = document.getElementById('count-left');
|
||
const btnSubmit = document.getElementById('btn-submit-intent');
|
||
const btnConfirm = document.getElementById('btn-confirm-final');
|
||
const cityModalOverlay = document.getElementById('city-modal-overlay');
|
||
const detailModalOverlay = document.getElementById('detail-modal-overlay');
|
||
|
||
// 1. 初始化槽位
|
||
function initSlots() {
|
||
slotsLeft.innerHTML = '';
|
||
slotsRight.innerHTML = '';
|
||
for (let i = 0; i < 3; i++) createSlot(i, slotsLeft);
|
||
createSlot(3, slotsRight);
|
||
}
|
||
|
||
function createSlot(index, container) {
|
||
const slot = document.createElement('div');
|
||
slot.className = 'slot-module empty';
|
||
slot.id = `slot-${index}`;
|
||
slot.onclick = () => removeJobByIndex(index);
|
||
|
||
const iconClass = index === 3 ? "fa-star text-yellow-500 opacity-30" : "fa-plus text-gray-500 opacity-20";
|
||
const text = index === 3 ? "最终确认位" : "意向岗位";
|
||
|
||
slot.innerHTML = `
|
||
<div class="slot-index">0${index+1}</div>
|
||
<i class="fa-solid ${iconClass} text-2xl mb-2"></i>
|
||
<div class="text-xs text-gray-600 font-bold tracking-widest">${text}</div>
|
||
`;
|
||
container.appendChild(slot);
|
||
}
|
||
|
||
// 2. 初始化筛选器
|
||
function initFilters() {
|
||
const types = ["全部", "生产类岗位", "服务类岗位"];
|
||
|
||
const quickCities = regionData["热门城市"].slice(0, 5);
|
||
const cityQuickContainer = document.getElementById('city-quick-select');
|
||
|
||
const renderQuickCities = () => {
|
||
cityQuickContainer.innerHTML = '';
|
||
quickCities.forEach(city => {
|
||
const btn = document.createElement('div');
|
||
btn.className = `city-btn ${filterState.city === city ? 'active' : ''}`;
|
||
btn.innerText = city;
|
||
btn.onclick = () => {
|
||
filterState.city = city;
|
||
renderQuickCities();
|
||
renderJobGrid();
|
||
};
|
||
cityQuickContainer.appendChild(btn);
|
||
});
|
||
|
||
const moreBtn = document.createElement('div');
|
||
moreBtn.className = 'more-city-btn';
|
||
moreBtn.innerHTML = `<span>${filterState.city === '全部' || quickCities.includes(filterState.city) ? '切换城市' : filterState.city}</span> <i class="fa-solid fa-location-crosshairs"></i>`;
|
||
moreBtn.onclick = openCityModal;
|
||
cityQuickContainer.appendChild(moreBtn);
|
||
};
|
||
renderQuickCities();
|
||
|
||
const typeContainer = document.getElementById('type-dropdown');
|
||
typeContainer.innerHTML = '';
|
||
types.forEach(type => {
|
||
const option = document.createElement('div');
|
||
option.className = `type-option ${type === '全部' ? 'selected' : ''}`;
|
||
option.innerHTML = `<span>${type}</span>`;
|
||
option.onclick = () => {
|
||
document.getElementById('current-type').innerText = type === '全部' ? '全部类型' : type;
|
||
document.querySelectorAll('.type-option').forEach(el => el.classList.remove('selected'));
|
||
option.classList.add('selected');
|
||
toggleTypeDropdown();
|
||
filterState.type = type;
|
||
renderJobGrid();
|
||
};
|
||
typeContainer.appendChild(option);
|
||
});
|
||
|
||
document.addEventListener('click', (e) => {
|
||
if (!document.getElementById('type-selector').contains(e.target)) {
|
||
document.getElementById('type-dropdown').classList.remove('show');
|
||
document.querySelector('.type-btn').classList.remove('active');
|
||
}
|
||
if (e.target === cityModalOverlay) closeCityModal();
|
||
if (e.target === detailModalOverlay) closeDetailModal();
|
||
});
|
||
}
|
||
|
||
function toggleTypeDropdown() {
|
||
document.getElementById('type-dropdown').classList.toggle('show');
|
||
document.querySelector('.type-btn').classList.toggle('active');
|
||
}
|
||
|
||
// --- 城市模态框 ---
|
||
function openCityModal() {
|
||
cityModalOverlay.classList.add('show');
|
||
renderRegionSidebar();
|
||
renderCityGrid(currentRegion);
|
||
}
|
||
function closeCityModal() { cityModalOverlay.classList.remove('show'); }
|
||
|
||
function renderRegionSidebar() {
|
||
const sidebar = document.getElementById('region-sidebar');
|
||
sidebar.innerHTML = '';
|
||
Object.keys(regionData).forEach(region => {
|
||
const item = document.createElement('li');
|
||
item.className = `region-item ${currentRegion === region ? 'active' : ''}`;
|
||
|
||
let icon = '';
|
||
if (region === '热门城市') icon = '<i class="fa-solid fa-fire text-red-500 mr-2"></i>';
|
||
else if (region === '直辖市') icon = '<i class="fa-solid fa-star text-yellow-500 mr-2"></i>';
|
||
item.innerHTML = `${icon}${region}`;
|
||
|
||
item.onclick = () => {
|
||
currentRegion = region;
|
||
renderRegionSidebar();
|
||
renderCityGrid(region);
|
||
document.querySelector('.city-grid-area').scrollTop = 0;
|
||
};
|
||
sidebar.appendChild(item);
|
||
});
|
||
}
|
||
|
||
function renderCityGrid(region) {
|
||
const grid = document.getElementById('city-grid');
|
||
grid.innerHTML = '';
|
||
const cities = regionData[region] || [];
|
||
|
||
cities.forEach(city => {
|
||
const item = document.createElement('div');
|
||
item.className = `city-option ${filterState.city === city ? 'selected' : ''}`;
|
||
item.innerText = city;
|
||
item.onclick = () => {
|
||
filterState.city = city;
|
||
closeCityModal();
|
||
initFilters();
|
||
renderJobGrid();
|
||
};
|
||
grid.appendChild(item);
|
||
});
|
||
}
|
||
|
||
// --- 详情模态框逻辑 (新增) ---
|
||
function openJobDetail(event, id) {
|
||
event.stopPropagation(); // 阻止冒泡,避免触发选卡
|
||
const job = allJobs.find(j => j.id === id);
|
||
if(!job) return;
|
||
|
||
// 填充数据
|
||
document.getElementById('detail-title').innerText = job.title;
|
||
document.getElementById('detail-salary').innerText = job.salary;
|
||
document.getElementById('detail-company').innerText = job.company;
|
||
document.getElementById('detail-type').innerText = job.type;
|
||
|
||
// 左侧参数
|
||
document.getElementById('detail-start-date').innerText = job.startDate || "待定";
|
||
document.getElementById('detail-duration').innerText = job.duration || "长期";
|
||
document.getElementById('detail-location').innerText = job.reportLocation || job.city;
|
||
document.getElementById('detail-roomboard').innerText = job.roomBoard || "不提供";
|
||
document.getElementById('detail-quota').innerText = `已招 ${job.occupied} / 共 ${job.quota} 人`;
|
||
|
||
// 右侧列表:岗位要求 (处理换行符)
|
||
const reqList = document.getElementById('detail-requirements');
|
||
reqList.innerHTML = '';
|
||
if(job.requirements) {
|
||
const reqs = job.requirements.split('\n');
|
||
reqs.forEach(req => {
|
||
if(req.trim()){
|
||
const li = document.createElement('li');
|
||
li.className = 'req-item';
|
||
li.innerText = req.trim();
|
||
reqList.appendChild(li);
|
||
}
|
||
});
|
||
} else {
|
||
reqList.innerHTML = '<li class="req-item">暂无详细要求</li>';
|
||
}
|
||
|
||
// 右侧列表:福利待遇
|
||
const benefitsContainer = document.getElementById('detail-benefits');
|
||
benefitsContainer.innerHTML = '';
|
||
if(job.benefits && job.benefits.length > 0) {
|
||
job.benefits.forEach(b => {
|
||
const tag = document.createElement('span');
|
||
tag.className = 'welfare-tag';
|
||
tag.innerText = b;
|
||
benefitsContainer.appendChild(tag);
|
||
});
|
||
} else {
|
||
benefitsContainer.innerHTML = '<span class="text-gray-500 text-sm">暂无福利信息</span>';
|
||
}
|
||
|
||
// 企业介绍
|
||
document.getElementById('detail-company-intro').innerText = job.companyIntro || "暂无企业介绍。";
|
||
|
||
// 显示
|
||
detailModalOverlay.classList.add('show');
|
||
}
|
||
|
||
function closeDetailModal() {
|
||
detailModalOverlay.classList.remove('show');
|
||
}
|
||
|
||
// 3. 渲染岗位网格 (包含详情按钮)
|
||
function renderJobGrid() {
|
||
jobGrid.innerHTML = '';
|
||
const filteredJobs = allJobs.filter(job => {
|
||
const matchCity = filterState.city === "全部" || job.city === filterState.city;
|
||
const matchType = filterState.type === "全部" || job.type === filterState.type;
|
||
return matchCity && matchType;
|
||
});
|
||
|
||
if (filteredJobs.length === 0) {
|
||
jobGrid.innerHTML = `<div class="col-span-3 text-center py-10 text-gray-600"><p>暂无匹配岗位</p></div>`;
|
||
return;
|
||
}
|
||
|
||
filteredJobs.forEach(job => {
|
||
const isSelected = selectedJobs.includes(job.id);
|
||
const card = document.createElement('div');
|
||
card.className = `job-card ${isSelected ? 'disabled' : ''}`;
|
||
card.id = `job-card-${job.id}`;
|
||
// 点击卡片本体还是选人
|
||
card.onclick = () => selectJob(job.id);
|
||
|
||
const typeClass = job.type === "生产类岗位" ? "type-produce" : "type-service";
|
||
const typeLabelHtml = `<span class="type-badge ${typeClass}">${job.type}</span>`;
|
||
const tagsHtml = job.tags.map(t => `<span class="tag">${t}</span>`).join('');
|
||
|
||
card.innerHTML = `
|
||
<div class="quota-badge">已选 ${job.occupied} / ${job.quota}</div>
|
||
<div class="card-top">
|
||
<div class="card-icon"><i class="fa-solid ${job.icon}"></i></div>
|
||
<div class="card-salary">${job.salary}</div>
|
||
</div>
|
||
<div>
|
||
<div class="card-title">${job.title}</div>
|
||
<div class="card-company"><i class="fa-regular fa-building"></i> ${job.company}</div>
|
||
<div class="card-tags">
|
||
${typeLabelHtml}
|
||
${tagsHtml}
|
||
</div>
|
||
</div>
|
||
<button class="btn-detail" onclick="openJobDetail(event, ${job.id})">
|
||
<i class="fa-regular fa-eye"></i> 查看详情
|
||
</button>
|
||
<div class="check-mark"><i class="fa-solid fa-circle-check"></i></div>
|
||
`;
|
||
jobGrid.appendChild(card);
|
||
});
|
||
}
|
||
|
||
// --- 核心交互逻辑 ---
|
||
function selectJob(id) {
|
||
if (selectedJobs.includes(id)) return;
|
||
if (selectedJobs.length >= MAX_SELECTION) {
|
||
gsap.fromTo([slotsLeft, slotsRight], { x: -5 }, { x: 0, duration: 0.4, ease: "elastic.out(1, 0.3)" });
|
||
return;
|
||
}
|
||
selectedJobs.push(id);
|
||
updateUI(true, id);
|
||
}
|
||
|
||
function removeJobByIndex(index) {
|
||
if (index >= selectedJobs.length) return;
|
||
const removedId = selectedJobs[index];
|
||
selectedJobs.splice(index, 1);
|
||
updateUI(false, removedId);
|
||
}
|
||
|
||
function updateUI(isAdding, targetId) {
|
||
for (let i = 0; i < MAX_SELECTION; i++) {
|
||
const slot = document.getElementById(`slot-${i}`);
|
||
const jobId = selectedJobs[i];
|
||
|
||
if (jobId) {
|
||
const job = allJobs.find(j => j.id === jobId);
|
||
if (slot.classList.contains('empty') || slot.dataset.lastId != jobId) {
|
||
slot.className = 'slot-module filled';
|
||
slot.dataset.lastId = jobId;
|
||
|
||
const isFinal = (i === 3);
|
||
const removeText = isFinal ? "撤销确认" : "移除意向";
|
||
|
||
slot.innerHTML = `
|
||
<div class="slot-index">0${i+1}</div>
|
||
<div class="slot-content-title">${job.title}</div>
|
||
<div class="slot-content-salary">${job.salary}</div>
|
||
<div class="slot-content-company">${job.company}</div>
|
||
<div class="slot-remove-mask">
|
||
<i class="fa-solid fa-xmark text-2xl mb-2"></i>
|
||
<span class="text-xs font-bold tracking-widest">${removeText}</span>
|
||
</div>
|
||
`;
|
||
|
||
if(isAdding && i === selectedJobs.length - 1) {
|
||
gsap.fromTo(slot, { y: -30, opacity: 0, scale: 0.8 }, { y: 0, opacity: 1, scale: 1, duration: 0.5, ease: "back.out(1.5)" });
|
||
}
|
||
}
|
||
} else {
|
||
if (!slot.classList.contains('empty')) {
|
||
slot.className = 'slot-module empty';
|
||
delete slot.dataset.lastId;
|
||
const isFinal = (i === 3);
|
||
const iconClass = isFinal ? "fa-star text-yellow-500 opacity-30" : "fa-plus text-gray-500 opacity-20";
|
||
const text = isFinal ? "最终确认位" : "意向岗位";
|
||
slot.innerHTML = `<div class="slot-index">0${i+1}</div><i class="fa-solid ${iconClass} text-2xl mb-2"></i><div class="text-xs text-gray-600 font-bold tracking-widest">${text}</div>`;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (targetId) {
|
||
const card = document.getElementById(`job-card-${targetId}`);
|
||
if (card) {
|
||
if (isAdding) {
|
||
card.classList.add('disabled');
|
||
gsap.to(card, { scale: 0.92, opacity: 0.3, duration: 0.3, ease: "power2.out" });
|
||
} else {
|
||
card.classList.remove('disabled');
|
||
gsap.to(card, { scale: 1, opacity: 1, duration: 0.4, ease: "back.out(1.2)", clearProps: "all" });
|
||
}
|
||
}
|
||
}
|
||
|
||
const hasIntent = selectedJobs.length > 0;
|
||
countLeft.innerText = Math.min(selectedJobs.length, 3);
|
||
if (hasIntent) btnSubmit.classList.add('active');
|
||
else btnSubmit.classList.remove('active');
|
||
|
||
const hasFinal = selectedJobs.length === 4;
|
||
if (hasFinal) btnConfirm.classList.add('active');
|
||
else btnConfirm.classList.remove('active');
|
||
}
|
||
|
||
initSlots();
|
||
initFilters();
|
||
renderJobGrid();
|
||
|
||
</script>
|
||
</body>
|
||
</html> |