Files
all-in-one-sys/3.html
KQL 61698639ef feat: 完成多多畅职就业服务平台核心功能开发
主要更新:
-  完成主题配色从暗色到亮蓝白配色的全面转换
-  实现高薪岗位页面及后端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>
2025-12-22 15:40:55 +08:00

842 lines
51 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>