岗位覆盖扩充至 26 个 + 图谱岗位子图模式
- 新增 8 岗位族(大数据/机器学习/机器人/数据治理/边缘计算/物联网安全/智能系统架构/隐私计算),词表 161→179 - 图谱详情面板新增'岗位子图'模式(只看该岗位及一跳邻居,徽标可退出) - 测试集扩至 176 条、匹配标注 52 对;real_jd_500 F1 99.82%;合成三项 100% 保持
This commit is contained in:
@@ -31,11 +31,22 @@ python main.py # 自动构建/加载图谱,启动服务并打开浏
|
||||
|
||||
```bash
|
||||
python scripts/generate_dataset.py
|
||||
# random.seed(2026) 固定:1375 条 JD(2019-2026,18 个岗位,15 城市,7 行业,8% 抄袭、噪声植入)、
|
||||
# 161 项技能词表(含 AIOps/微前端等新技能)、144 条测试集+标准答案、36 对人岗匹配标注、
|
||||
# random.seed(2026) 固定:1720 条 JD(2019-2026,26 个岗位,15 城市,7 行业,8% 抄袭、噪声植入)、
|
||||
# 179 项技能词表(含 AIOps/微前端/ROS/SLAM/联邦学习/零信任等新技能)、176 条测试集+标准答案、
|
||||
# 52 对人岗匹配标注、
|
||||
# 7 份简历样例(txt,另附 resume_full.docx / resume_full.pdf 演示上传解析)、3 份行业报告样例、7 条审核留痕种子记录
|
||||
```
|
||||
|
||||
岗位清单(26 个,对齐人工智能/大数据/智能系统/物联网等新一代信息技术领域):
|
||||
|
||||
- 后端与前端:Java开发工程师、Python开发工程师、Go开发工程师、前端工程师
|
||||
- 大数据:数据工程师、大数据开发工程师、数据治理工程师
|
||||
- AI 与算法:算法工程师、机器学习工程师、NLP工程师、CV工程师、智能机器人算法工程师、隐私计算工程师
|
||||
- 智能系统与云原生:云原生工程师、智能系统架构师、边缘计算工程师
|
||||
- 物联网与嵌入式:嵌入式工程师、物联网工程师、物联网安全工程师
|
||||
- 测试/运维/分析/产品:测试开发工程师、运维工程师(SRE)、数据分析师、AI产品经理
|
||||
- 新岗位发现入库(2023 年后出现):大模型应用工程师、RAG工程师、提示词工程师
|
||||
|
||||
简历上传支持 .txt / .docx / .pdf(`POST /api/resume/parse/file`,≤5MB,解析即脱敏,临时文件自动清理)。
|
||||
|
||||
## 真实数据集(data/real/,与合成集分开报告)
|
||||
@@ -62,18 +73,18 @@ python scripts/evaluate.py
|
||||
|
||||
| 指标 | 实测 | 目标 |
|
||||
|---|---|---|
|
||||
| JD 解析准确率 | 100% | ≥90% |
|
||||
| JD 解析准确率 | 100% (176 条) | ≥90% |
|
||||
| 简历提取准确率 | 100% (7 份简历) | ≥90% |
|
||||
| 人岗匹配准确率 | 100% (36 对标注) | ≥90% |
|
||||
| 真实 JD(AutoWeakS)技能 F1 | 99.94% (200 条) | ≥80% |
|
||||
| 真实简历(AIStudio)姓名/电话命中 | 100% / 100% (50 份) | 100% |
|
||||
| 真实简历(AIStudio)技能 F1 | 100% (50 份) | — |
|
||||
| 人岗匹配准确率 | 100% (52 对标注) | ≥90% |
|
||||
| 真实 JD(AutoWeakS)技能 F1 | 99.80% (200 条) / 99.82% (500 条分层) | ≥80% |
|
||||
| 真实简历(AIStudio)姓名/电话命中 | 100% / 100% (200 份) | 100% |
|
||||
| 真实简历(AIStudio)技能 F1 | 100% (200 份) | — |
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
python -m pytest tests/ --cov=app --cov-report=term-missing
|
||||
# 71 passed
|
||||
# 74 passed
|
||||
```
|
||||
|
||||
端到端冒烟(不启动服务):
|
||||
|
||||
+103
-7
@@ -27,7 +27,7 @@
|
||||
"f1": 1.0
|
||||
}
|
||||
},
|
||||
"samples": 144,
|
||||
"samples": 176,
|
||||
"bad_lines": 0
|
||||
},
|
||||
"resume_detail": {
|
||||
@@ -262,6 +262,102 @@
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "大数据开发工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "大数据开发工程师",
|
||||
"expected": false,
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "机器学习工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "机器学习工程师",
|
||||
"expected": false,
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "智能机器人算法工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "智能机器人算法工程师",
|
||||
"expected": false,
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "数据治理工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "数据治理工程师",
|
||||
"expected": false,
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "边缘计算工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "边缘计算工程师",
|
||||
"expected": false,
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "物联网安全工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "物联网安全工程师",
|
||||
"expected": false,
|
||||
"score": 6.67,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "智能系统架构师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "智能系统架构师",
|
||||
"expected": false,
|
||||
"score": 4.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "隐私计算工程师",
|
||||
"expected": true,
|
||||
"score": 75.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "隐私计算工程师",
|
||||
"expected": false,
|
||||
"score": 5.0,
|
||||
"ok": true
|
||||
},
|
||||
{
|
||||
"job": "前端工程师",
|
||||
"expected": true,
|
||||
@@ -300,13 +396,13 @@
|
||||
}
|
||||
],
|
||||
"real_jd": {
|
||||
"accuracy": 0.9995,
|
||||
"accuracy": 0.9984,
|
||||
"title_accuracy": 1.0,
|
||||
"fields": {
|
||||
"required_skills": {
|
||||
"precision": 1.0,
|
||||
"precision": 0.9976,
|
||||
"recall": 0.9988,
|
||||
"f1": 0.9994
|
||||
"f1": 0.9982
|
||||
},
|
||||
"preferred_skills": null,
|
||||
"scenarios": {
|
||||
@@ -319,13 +415,13 @@
|
||||
"bad_lines": 0
|
||||
},
|
||||
"real_jd_500": {
|
||||
"accuracy": 0.9991,
|
||||
"accuracy": 0.9983,
|
||||
"title_accuracy": 1.0,
|
||||
"fields": {
|
||||
"required_skills": {
|
||||
"precision": 1.0,
|
||||
"precision": 0.998,
|
||||
"recall": 0.998,
|
||||
"f1": 0.999
|
||||
"f1": 0.998
|
||||
},
|
||||
"preferred_skills": null,
|
||||
"scenarios": {
|
||||
|
||||
@@ -57,10 +57,10 @@ SKILLS = [
|
||||
("Struts", "后端框架", [], "L1", ["Java"]), ("Servlet", "后端框架", [], "L1", ["Java"]),
|
||||
("JSP", "后端框架", [], "L1", ["Java"]), ("SSM", "后端框架", [], "L2", ["Spring", "MyBatis"]),
|
||||
# 前端
|
||||
("Vue", "前端", ["Vue.js"], "L2", ["JavaScript"]), ("React", "前端", ["React.js"], "L2", ["JavaScript"]),
|
||||
("Vue", "前端", ["Vue.js"], "L2", ["JavaScript"]), ("React", "前端", ["React.js", "ReactJS"], "L2", ["JavaScript"]),
|
||||
("Angular", "前端", ["AngularJS"], "L2", ["TypeScript"]), ("jQuery", "前端", [], "L1", ["JavaScript"]),
|
||||
("Webpack", "前端", [], "L2", ["JavaScript"]), ("Vite", "前端", [], "L2", ["JavaScript"]),
|
||||
("Node.js", "前端", ["Node"], "L2", ["JavaScript"]), ("小程序开发", "前端", ["微信小程序"], "L2", ["JavaScript"]),
|
||||
("Node.js", "前端", ["Node", "NodeJS"], "L2", ["JavaScript"]), ("小程序开发", "前端", ["微信小程序"], "L2", ["JavaScript"]),
|
||||
("Element UI", "前端", [], "L1", ["Vue"]), ("Ant Design", "前端", [], "L1", ["React"]),
|
||||
("HTML", "前端", ["HTML5"], "L1", []), ("CSS", "前端", ["CSS3"], "L1", []),
|
||||
("Tailwind CSS", "前端", [], "L2", ["CSS"]), ("低代码平台", "前端", [], "L2", []),
|
||||
@@ -141,6 +141,28 @@ SKILLS = [
|
||||
("Tableau", "数据分析", [], "L2", ["数据可视化"]), ("Power BI", "数据分析", ["PowerBI"], "L2", ["数据可视化"]),
|
||||
("SPSS", "数据分析", [], "L2", []), ("数据可视化", "数据分析", [], "L2", ["数据分析"]),
|
||||
("A/B测试", "数据分析", ["AB测试"], "L2", ["数据分析"]),
|
||||
# 机器人与智能系统(岗位覆盖扩展)
|
||||
("ROS", "AI算法", ["机器人操作系统"], "L2", ["C++", "Linux"]),
|
||||
("SLAM", "AI算法", ["同步定位与建图"], "L3", ["计算机视觉"]),
|
||||
("路径规划", "AI算法", [], "L3", ["机器学习"]),
|
||||
# 数据治理
|
||||
("数据治理", "大数据", [], "L2", ["SQL"]),
|
||||
("数据脱敏", "大数据", ["数据遮蔽"], "L2", ["SQL"]),
|
||||
("元数据管理", "大数据", ["元数据"], "L2", ["数据治理"]),
|
||||
("数据血缘", "大数据", [], "L3", ["元数据管理"]),
|
||||
("湖仓一体", "大数据", ["实时湖仓"], "L3", ["Hive"]),
|
||||
# 边缘计算
|
||||
("边缘推理", "物联网", ["边缘推理框架"], "L3", ["边缘计算", "深度学习"]),
|
||||
("TensorRT", "AI算法", [], "L3", ["深度学习"]),
|
||||
("KubeEdge", "云原生", [], "L3", ["Kubernetes", "边缘计算"]),
|
||||
# 安全与隐私计算
|
||||
("零信任", "安全", ["零信任架构"], "L3", []),
|
||||
("入侵检测", "安全", [], "L2", ["Linux"]),
|
||||
("等保合规", "安全", [], "L2", []),
|
||||
("联邦学习", "AI算法", [], "L3", ["机器学习"]),
|
||||
("多方安全计算", "安全", ["MPC"], "L3", []),
|
||||
("同态加密", "安全", [], "L3", []),
|
||||
("差分隐私", "安全", [], "L3", ["机器学习"]),
|
||||
]
|
||||
|
||||
SCENARIOS = ["电商", "金融", "物流", "社交", "教育", "医疗", "政务", "游戏",
|
||||
@@ -152,11 +174,11 @@ INDUSTRIES = ["互联网/软件", "人工智能", "金融科技", "智能制造"
|
||||
|
||||
# ---------------------------------------------------------------- 岗位原型
|
||||
def arc(title, category, years, salary, scenarios, industries,
|
||||
duty_tmpls, duty_objs, timeline, volumes):
|
||||
duty_tmpls, duty_objs, timeline, volumes, test_n=8):
|
||||
return dict(title=title, category=category, years=years, salary=salary,
|
||||
scenarios=scenarios, industries=industries,
|
||||
duty_tmpls=duty_tmpls, duty_objs=duty_objs,
|
||||
timeline=timeline, volumes=volumes)
|
||||
timeline=timeline, volumes=volumes, test_n=test_n)
|
||||
|
||||
BACKEND_DUTIES = ["负责{o}的设计与开发", "参与{o}的性能优化与重构", "配合产品团队完成{o}的需求评审",
|
||||
"编写{o}的技术文档与接口文档", "承担{o}的线上问题排查与修复", "推动{o}的代码评审与质量改进"]
|
||||
@@ -325,6 +347,69 @@ ARCHETYPES = [
|
||||
["营销文案生成", "智能客服话术", "内容创作", "教学辅导", "商品描述", "对话流程"],
|
||||
[(2024, 2026, ["提示词工程", "大模型应用开发", "Python", "数据分析"], ["LangChain", "RAG", "AI产品"])],
|
||||
{2024: 2, 2025: 6, 2026: 10}),
|
||||
# —— 岗位覆盖扩展(8 个既有岗位族,2019 年前已存在,支撑赛题领域覆盖) ——
|
||||
# 大数据开发:2019-2021 Hadoop 离线系 → 2022+ Flink 实时/湖仓一体(演化信号)
|
||||
arc("大数据开发工程师", "大数据", 3, "16K-32K", ["电商", "金融", "短视频"],
|
||||
["互联网/软件", "金融科技"],
|
||||
["负责{o}的离线计算任务开发", "搭建{o}的实时计算链路", "参与{o}的数据模型设计",
|
||||
"优化{o}的任务性能与资源消耗", "维护{o}的调度依赖与数据质量"],
|
||||
["离线数仓", "实时数仓", "用户行为日志", "流量数据", "交易数据", "数据集市"],
|
||||
[(2019, 2021, ["SQL", "Hadoop", "Hive", "Spark", "HDFS"], ["Sqoop", "HBase", "Scala"]),
|
||||
(2022, 2026, ["SQL", "Spark", "Flink", "Kafka", "Hive"], ["ClickHouse", "Doris", "湖仓一体"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
# 机器学习工程师:2023+ 加分项出现"模型微调/大模型应用开发"(演化信号)
|
||||
arc("机器学习工程师", "AI算法", 3, "18K-35K", ["金融", "电商", "广告"],
|
||||
["人工智能"],
|
||||
["负责{o}的特征工程与模型训练", "参与{o}的离线评估与线上实验", "优化{o}的推理性能与资源占用",
|
||||
"建设{o}的样本与特征流水线", "跟进{o}的算法迭代与复盘"],
|
||||
["风控模型", "搜索排序", "召回策略", "销量预测", "异常检测", "用户分层"],
|
||||
[(2019, 2022, ["Python", "机器学习", "Scikit-learn", "SQL", "XGBoost"], ["LightGBM", "TensorFlow", "Spark"]),
|
||||
(2023, 2026, ["Python", "机器学习", "深度学习", "PyTorch", "XGBoost"], ["模型微调", "大模型应用开发", "LightGBM"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
arc("智能机器人算法工程师", "AI算法", 3, "20K-40K", ["智能制造", "物流", "出行"],
|
||||
["人工智能", "智能制造"],
|
||||
["负责{o}的算法研发与调优", "参与{o}的仿真验证与实机部署", "优化{o}的实时性与鲁棒性",
|
||||
"建设{o}的数据闭环与评测体系", "跟进{o}的前沿方案并落地"],
|
||||
["SLAM 建图", "定位导航", "运动规划", "多传感器融合", "机械臂控制", "避障策略"],
|
||||
[(2019, 2026, ["C++", "Python", "ROS", "SLAM", "路径规划"], ["深度学习", "强化学习", "OpenCV"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
arc("数据治理工程师", "大数据", 3, "15K-30K", ["金融", "政务", "医疗"],
|
||||
["金融科技", "互联网/软件"],
|
||||
["负责{o}的标准制定与落地", "建设{o}的元数据与血缘体系", "推进{o}的质量监控与整改",
|
||||
"制定{o}的分级分类与脱敏策略", "组织{o}的盘点与确权工作"],
|
||||
["数据资产", "主数据", "指标口径", "敏感数据", "数据目录", "质量规则"],
|
||||
[(2019, 2026, ["SQL", "数据治理", "元数据管理", "数据脱敏", "Python"], ["数据血缘", "Hive", "Spark"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
arc("边缘计算工程师", "物联网", 3, "16K-30K", ["智能制造", "安防", "物流"],
|
||||
["物联网", "智能制造"],
|
||||
["负责{o}的部署与调优", "参与{o}的模型压缩与推理加速", "建设{o}的云边协同链路",
|
||||
"优化{o}的资源占用与稳定性", "编写{o}的技术文档"],
|
||||
["边缘网关", "边缘节点", "端侧推理", "视频分析", "设备接入", "边云协同"],
|
||||
[(2019, 2021, ["C++", "Python", "Linux", "MQTT", "嵌入式开发"], ["Docker", "边缘计算", "OpenCV"]),
|
||||
(2022, 2026, ["Python", "边缘计算", "Docker", "Linux", "MQTT"], ["边缘推理", "TensorRT", "KubeEdge"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
arc("物联网安全工程师", "物联网", 3, "16K-32K", ["智能制造", "安防", "政务"],
|
||||
["物联网", "智慧城市"],
|
||||
["负责{o}的安全评估与加固", "建设{o}的入侵检测与响应体系", "参与{o}的固件安全审计",
|
||||
"制定{o}的接入认证策略", "跟踪{o}的漏洞情报与处置"],
|
||||
["设备接入", "设备网络", "边缘网关", "固件", "通信协议", "密钥体系"],
|
||||
[(2019, 2026, ["Linux", "Python", "入侵检测", "零信任", "MQTT"], ["等保合规", "嵌入式开发", "云计算"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
arc("智能系统架构师", "云原生", 5, "30K-60K·14薪", ["金融", "政务", "智能制造"],
|
||||
["互联网/软件", "人工智能"],
|
||||
["负责{o}的整体架构设计", "主导{o}的技术选型与评审", "制定{o}的容量与容灾方案",
|
||||
"推动{o}的架构演进与落地", "沉淀{o}的架构规范与最佳实践"],
|
||||
["智能平台", "业务中台", "云边端协同", "高可用体系", "多云架构", "技术路线"],
|
||||
[(2019, 2021, ["分布式系统", "微服务", "云计算", "高并发", "Linux"], ["Docker", "Kubernetes", "Service Mesh"]),
|
||||
(2022, 2026, ["分布式系统", "微服务", "Kubernetes", "云计算", "高并发"], ["Service Mesh", "边缘计算", "物联网平台"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
arc("隐私计算工程师", "AI算法", 4, "25K-45K", ["金融", "政务", "医疗"],
|
||||
["人工智能", "金融科技"],
|
||||
["负责{o}的方案设计与实现", "参与{o}的性能优化与工程化", "建设{o}的协同计算链路",
|
||||
"评估{o}的安全性与合规性", "跟进{o}的最新研究成果"],
|
||||
["联邦建模", "多方联合统计", "安全求交", "数据协作", "密文计算", "隐私保护"],
|
||||
[(2019, 2026, ["Python", "机器学习", "联邦学习", "同态加密", "多方安全计算"], ["差分隐私", "模型微调", "Spark"])],
|
||||
{2019: 4, 2020: 4, 2021: 4, 2022: 5, 2023: 5, 2024: 6, 2025: 6, 2026: 6}, test_n=4),
|
||||
]
|
||||
|
||||
COMPANY_PRE = ["星澜", "云启", "数联", "智汇", "蓝湾", "极光", "恒宇", "锐思", "博远", "辰安",
|
||||
@@ -779,8 +864,8 @@ def main() -> None:
|
||||
# 5. 测试集(与 raw 不同 jid 命名空间)+ 标准答案
|
||||
testset, gold = [], {}
|
||||
tseq = 0
|
||||
per_arc = 8 # 18 岗位 × 8 = 144 条 >= 120
|
||||
for a in ARCHETYPES:
|
||||
per_arc = a["test_n"] # 原 18 岗位 × 8 + 扩展 8 岗位 × 4 = 176 条 >= 120
|
||||
years = sorted(a["volumes"])
|
||||
for _ in range(per_arc):
|
||||
tseq += 1
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
REM ============================================================
|
||||
REM CareerNexus 职联 启动脚本(Windows)
|
||||
REM 直接通过项目内 .venv 启动服务,自动打开浏览器
|
||||
REM 首次使用请先运行 init.bat
|
||||
REM ============================================================
|
||||
cd /d "%~dp0"
|
||||
|
||||
if not exist ".venv\Scripts\python.exe" (
|
||||
echo [错误] 未找到 .venv,请先运行 init.bat 完成初始化
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo 启动 CareerNexus 职联 ...
|
||||
echo 服务地址 http://127.0.0.1:9008 (关闭本窗口即停止服务)
|
||||
.venv\Scripts\python.exe main.py
|
||||
pause
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# CareerNexus 职联 启动脚本(Linux / macOS / Git Bash)
|
||||
# 直接通过项目内 .venv 启动服务,自动打开浏览器
|
||||
# 首次使用请先运行 bash init.sh
|
||||
# ============================================================
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Windows venv 与 POSIX venv 路径兼容
|
||||
if [ -f ".venv/bin/python" ]; then
|
||||
VENV_PY=.venv/bin/python
|
||||
elif [ -f ".venv/Scripts/python.exe" ]; then
|
||||
VENV_PY=.venv/Scripts/python.exe
|
||||
else
|
||||
echo "[错误] 未找到 .venv,请先运行 bash init.sh 完成初始化"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "启动 CareerNexus 职联 ..."
|
||||
echo "服务地址 http://127.0.0.1:9008 (Ctrl+C 停止服务)"
|
||||
exec "$VENV_PY" main.py
|
||||
@@ -14,6 +14,7 @@
|
||||
filters: { types: { Job: true, Skill: true, Industry: true, Scenario: true }, category: "", level: "",
|
||||
edges: { required: true, preferred: true, applies: true, belongs: true } },
|
||||
selected: null, // 选中节点 id
|
||||
soloJob: null, // 岗位子图模式:只看该岗位及其一跳邻居
|
||||
hover: null,
|
||||
navStack: [], // 详情面板导航栈
|
||||
drag: null, // {mode:'pan'|'node', ...}
|
||||
@@ -161,6 +162,9 @@
|
||||
}
|
||||
function visible(n) {
|
||||
if (!G.filters.types[n.type]) return false;
|
||||
if (G.soloJob) { // 岗位子图:只保留该岗位与一跳邻居
|
||||
if (n.id !== G.soloJob && !neighborsOf(G.soloJob).has(n.id)) return false;
|
||||
}
|
||||
if (n.type === "Job") {
|
||||
if (G.filters.category && n.category !== G.filters.category) return false;
|
||||
if (G.filters.level && jobLevel(n) !== G.filters.level) return false;
|
||||
@@ -597,6 +601,18 @@
|
||||
}
|
||||
CN.gotoNode = locateNode;
|
||||
|
||||
function updateSoloBadge() {
|
||||
const badge = $("#solo-badge");
|
||||
if (!badge) return;
|
||||
badge.classList.toggle("hidden", !G.soloJob);
|
||||
if (G.soloJob) {
|
||||
const n = G.nodes[G.idx[G.soloJob]];
|
||||
$("#solo-badge-text").textContent = `岗位子图:${(n && (n.title || n.id)) || G.soloJob}`;
|
||||
}
|
||||
}
|
||||
const soloExit = $("#solo-exit");
|
||||
if (soloExit) soloExit.onclick = () => { G.soloJob = null; updateSoloBadge(); requestDraw(); };
|
||||
|
||||
/* ================= 备注(localStorage) ================= */
|
||||
const getNotes = () => JSON.parse(localStorage.getItem(NOTES_KEY) || "{}");
|
||||
function nodeNotes(id) { return getNotes()[id] || []; }
|
||||
@@ -720,6 +736,7 @@
|
||||
<button class="btn btn-secondary btn-sm" id="dt-sources">查看来源</button>`}
|
||||
<button class="btn btn-secondary btn-sm" id="dt-note">添加备注</button>
|
||||
${n.type === "Job" && !isPending ? `<button class="btn btn-secondary btn-sm" id="dt-evo">查看演化</button>
|
||||
<button class="btn btn-secondary btn-sm" id="dt-solo">岗位子图</button>
|
||||
<button class="btn btn-primary btn-sm full" id="dt-learn">生成学习路径</button>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
@@ -765,6 +782,13 @@
|
||||
$("#evo-job-select").value = id;
|
||||
$("#evo-job-select").dispatchEvent(new Event("change"));
|
||||
};
|
||||
const soloBtn = $("#dt-solo");
|
||||
if (soloBtn) soloBtn.onclick = () => {
|
||||
G.soloJob = G.soloJob === id ? null : id;
|
||||
updateSoloBadge();
|
||||
requestDraw();
|
||||
if (G.soloJob) toast(`已进入「${n.title || id}」岗位子图(只看该岗位与一跳技能/场景/行业)`, "success");
|
||||
};
|
||||
const learnBtn = $("#dt-learn");
|
||||
if (learnBtn) learnBtn.onclick = () => {
|
||||
CN.switchView("path");
|
||||
|
||||
@@ -135,6 +135,12 @@
|
||||
<div class="graph-canvas-wrap card" id="graph-canvas-wrap">
|
||||
<canvas id="graph-canvas"></canvas>
|
||||
|
||||
<!-- 岗位子图状态徽标 -->
|
||||
<div class="solo-badge hidden" id="solo-badge">
|
||||
<span id="solo-badge-text">岗位子图</span>
|
||||
<button id="solo-exit" title="退出岗位子图">×</button>
|
||||
</div>
|
||||
|
||||
<!-- 左上浮动工具条 -->
|
||||
<div class="graph-toolbar">
|
||||
<button class="tool-btn" id="tool-zoom-in" title="放大"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35M11 8v6M8 11h6"/></svg></button>
|
||||
|
||||
@@ -911,3 +911,16 @@ svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width:
|
||||
border-top: 1px solid var(--border);
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
/* 岗位子图状态徽标 */
|
||||
.solo-badge {
|
||||
position: absolute; top: 14px; left: 50%; transform: translateX(-50%);
|
||||
display: flex; align-items: center; gap: 8px; z-index: 30;
|
||||
background: var(--primary); color: #fff; font-size: 12px;
|
||||
border-radius: 999px; padding: 4px 8px 4px 14px; box-shadow: var(--shadow-pop);
|
||||
}
|
||||
.solo-badge button {
|
||||
border: none; background: rgba(255,255,255,.25); color: #fff;
|
||||
width: 18px; height: 18px; border-radius: 50%; cursor: pointer; line-height: 1;
|
||||
}
|
||||
.solo-badge button:hover { background: rgba(255,255,255,.45); }
|
||||
|
||||
Reference in New Issue
Block a user