职级视图再修:技能节点补词表 level 分带(此前全部落中级带),带间距拉大、技能/岗位分行限排,宽高比 5.2→2.4
This commit is contained in:
+8
-1
@@ -327,9 +327,16 @@ def restart_service() -> dict:
|
|||||||
|
|
||||||
@app.get("/api/graph")
|
@app.get("/api/graph")
|
||||||
def graph() -> dict:
|
def graph() -> dict:
|
||||||
"""返回节点与边(含时间区间)与演化事件,供前端渲染全景图谱。"""
|
"""返回节点与边(含时间区间)与演化事件,供前端渲染全景图谱。
|
||||||
|
|
||||||
|
技能节点附带词表 level(L1/L2/L3),前端职级演进视图按此分带。
|
||||||
|
"""
|
||||||
kg = _get_kg()
|
kg = _get_kg()
|
||||||
nodes = [{"id": n, **d} for n, d in kg.g.nodes(data=True)]
|
nodes = [{"id": n, **d} for n, d in kg.g.nodes(data=True)]
|
||||||
|
skill_lv = {s["name"]: s.get("level") for s in load_lexicon(resolve(_cfg, "lexicon_path"))["skills"]}
|
||||||
|
for n in nodes:
|
||||||
|
if n.get("type") == "Skill" and not n.get("level"):
|
||||||
|
n["level"] = skill_lv.get(n["id"], "L2")
|
||||||
edges = [{"source": u, "target": v, **d} for u, v, d in kg.g.edges(data=True)]
|
edges = [{"source": u, "target": v, **d} for u, v, d in kg.g.edges(data=True)]
|
||||||
events = sorted((e.model_dump() for e in kg.events), key=lambda e: e["window"])
|
events = sorted((e.model_dump() for e in kg.events), key=lambda e: e["window"])
|
||||||
return _ok({"nodes": nodes, "edges": edges, "events": events, "stats": kg.stats()})
|
return _ok({"nodes": nodes, "edges": edges, "events": events, "stats": kg.stats()})
|
||||||
|
|||||||
+8
-8
@@ -99,9 +99,9 @@
|
|||||||
/* ================= 视图切换:全景 / 技术栈 / 职级演进 / 动态时序 ================= */
|
/* ================= 视图切换:全景 / 技术栈 / 职级演进 / 动态时序 ================= */
|
||||||
const VIEW_NAMES = { overview: "全景总览", tech: "技术栈视图", level: "职级演进视图", timeline: "动态时序视图" };
|
const VIEW_NAMES = { overview: "全景总览", tech: "技术栈视图", level: "职级演进视图", timeline: "动态时序视图" };
|
||||||
const LEVEL_BANDS = [
|
const LEVEL_BANDS = [
|
||||||
{ name: "初级 L1 · ≤2 年", y: -340 },
|
{ name: "初级 L1 · ≤2 年", y: -420 },
|
||||||
{ name: "中级 L2 · 3-5 年", y: 0 },
|
{ name: "中级 L2 · 3-5 年", y: 0 },
|
||||||
{ name: "高级 L3 · >5 年", y: 340 },
|
{ name: "高级 L3 · >5 年", y: 420 },
|
||||||
];
|
];
|
||||||
|
|
||||||
/* 技术栈视图:类别锚点网格分布,岗位聚在锚点旁,技能/场景/行业靠向所属岗位质心 */
|
/* 技术栈视图:类别锚点网格分布,岗位聚在锚点旁,技能/场景/行业靠向所属岗位质心 */
|
||||||
@@ -153,19 +153,19 @@
|
|||||||
const yBand = LEVEL_BANDS[b].y;
|
const yBand = LEVEL_BANDS[b].y;
|
||||||
const jl = G.nodes.filter((n) => n.type === "Job" && levelBandOf(n) === b)
|
const jl = G.nodes.filter((n) => n.type === "Job" && levelBandOf(n) === b)
|
||||||
.sort((x, y) => String(x.title || x.id).localeCompare(String(y.title || y.id), "zh"));
|
.sort((x, y) => String(x.title || x.id).localeCompare(String(y.title || y.id), "zh"));
|
||||||
// 岗位带内多行排列(一行排开会横向过长,fitView 按比例缩成横带)
|
// 岗位在带线下方(最多 2 行),技能在带线上方(最多 3 行),两者不重叠
|
||||||
const jPerRow = Math.max(3, Math.ceil(Math.sqrt(jl.length) * 1.8));
|
const jPerRow = Math.max(3, Math.ceil(jl.length / 2));
|
||||||
jl.forEach((n, i) => {
|
jl.forEach((n, i) => {
|
||||||
n.x = ((i % jPerRow) - (jPerRow - 1) / 2) * 190;
|
n.x = ((i % jPerRow) - (jPerRow - 1) / 2) * 190;
|
||||||
n.y = yBand + 150 - Math.floor(i / jPerRow) * 85;
|
n.y = yBand + 90 + Math.floor(i / jPerRow) * 85;
|
||||||
n.vx = 0; n.vy = 0;
|
n.vx = 0; n.vy = 0;
|
||||||
});
|
});
|
||||||
const sl = G.nodes.filter((n) => n.type !== "Job" && levelBandOf(n) === b)
|
const sl = G.nodes.filter((n) => n.type !== "Job" && levelBandOf(n) === b)
|
||||||
.sort((x, y) => String(x.title || x.id).localeCompare(String(y.title || y.id), "zh"));
|
.sort((x, y) => String(x.title || x.id).localeCompare(String(y.title || y.id), "zh"));
|
||||||
const perRow = Math.max(4, Math.ceil(Math.sqrt(sl.length) * 1.9));
|
const perRow = Math.max(4, Math.ceil(sl.length / 4));
|
||||||
sl.forEach((n, i) => {
|
sl.forEach((n, i) => {
|
||||||
n.x = ((i % perRow) - (perRow - 1) / 2) * 108;
|
n.x = ((i % perRow) - (perRow - 1) / 2) * 100;
|
||||||
n.y = yBand - 120 + Math.floor(i / perRow) * 72;
|
n.y = yBand - 170 + Math.floor(i / perRow) * 62;
|
||||||
n.vx = 0; n.vy = 0;
|
n.vx = 0; n.vy = 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user