前端体验升级专项:驾驶舱T型Hero+趋势折线+播报轮播、图谱骨架屏与右键菜单、双雷达图、主色#1890FF统一、空状态补齐、移除调试探针
This commit is contained in:
@@ -111,6 +111,53 @@ eq(d.del.map((r) => r.skill).join(","), "JSP", "diff 退出");
|
||||
eq(d.keep.length, 1, "diff 保留数");
|
||||
eq(d.keep[0].up, true, "加分升级必备标记");
|
||||
|
||||
/* easeOutCubic:count-up 缓动曲线(0→1,中点已过 87.5%) */
|
||||
const easeOutCubic = loadFn("easeOutCubic");
|
||||
eq(easeOutCubic(0), 0, "缓动起点为 0");
|
||||
eq(easeOutCubic(1), 1, "缓动终点为 1");
|
||||
eq(easeOutCubic(0.5), 0.875, "缓动中点 easeOut 特征");
|
||||
eq(easeOutCubic(0.25) < easeOutCubic(0.5), true, "缓动单调递增");
|
||||
|
||||
/* trendSeries:近 n 季度事件序列(总量 + 四类计数) */
|
||||
const trendSeries = new Function(
|
||||
`const CHANGE_META = { "新增": {}, "增强": {}, "减弱": {}, "退出": {} };\n${extractFn("trendSeries")}; return trendSeries;`)();
|
||||
const tsEvts = [
|
||||
...Array.from({ length: 9 }, (_, i) => ({ window: `20${10 + i}Q1`, change_type: "新增" })),
|
||||
{ window: "2018Q1", change_type: "增强" },
|
||||
{ window: "2018Q1", change_type: "退出" },
|
||||
{ window: "2018Q1", change_type: "其他" },
|
||||
];
|
||||
const ts = trendSeries(tsEvts, 8);
|
||||
eq(ts.length, 8, "趋势序列只取近 8 季");
|
||||
eq(ts[7].window, "2018Q1", "最近窗口在序列末尾");
|
||||
eq(ts[7].total, 4, "末季总量含未分类事件");
|
||||
eq(ts[7].byType["新增"], 1, "末季新增计数");
|
||||
eq(ts[7].byType["增强"], 1, "末季增强计数");
|
||||
eq(ts[7].byType["退出"], 1, "末季退出计数");
|
||||
eq(trendSeries([], 8).length, 0, "空事件空序列");
|
||||
|
||||
/* computeReplaceBuckets:能力更替三桶(旧核心 / 持续 / 新核心,近 4 季度口径) */
|
||||
const computeReplaceBuckets = loadFn("computeReplaceBuckets");
|
||||
const rpWins = ["2025Q2", "2025Q3", "2025Q4", "2026Q1", "2026Q2", "2026Q3"];
|
||||
const rpEdges = [
|
||||
{ target: "JSP", intervals: [["2020Q1", "2025Q4"]] }, // 已退出 → 旧核心
|
||||
{ target: "Java", intervals: [["2020Q1", null]] }, // 持续
|
||||
{ target: "Kubernetes", intervals: [["2026Q1", null]] }, // 近 4 季进入 → 新核心
|
||||
{ target: "Docker", intervals: [["2025Q2", null]] }, // 起点不在近 4 季 → 持续
|
||||
];
|
||||
const rp = computeReplaceBuckets(rpEdges, rpWins);
|
||||
eq(rp.oldCore.map((e) => e.target).join(","), "JSP", "旧核心=已退出");
|
||||
eq(rp.cont.map((e) => e.target).join(","), "Java,Docker", "持续=生效且非近期进入");
|
||||
eq(rp.newCore.map((e) => e.target).join(","), "Kubernetes", "新核心=近 4 季进入");
|
||||
eq(computeReplaceBuckets(rpEdges, []).newCore.length, 0, "无窗口时无新核心");
|
||||
|
||||
/* radarValues:双雷达候选人四维归一化(缺失 0 / 超界截断) */
|
||||
const radarValues = new Function(
|
||||
`const RADAR_KEYS = ["required", "preferred", "experience", "scenario"];\n${extractFn("radarValues")}; return radarValues;`)();
|
||||
eq(radarValues({ required: 80, preferred: 50, experience: 100, scenario: 0 }).join(","), "0.8,0.5,1,0", "雷达四维映射");
|
||||
eq(radarValues({ required: 120 }).join(","), "1,0,0,0", "雷达超界截断");
|
||||
eq(radarValues(null).join(","), "0,0,0,0", "雷达空对象兜底");
|
||||
|
||||
/* graph.js 纯逻辑:时序边生效判定 / 职级带归属(函数在 IIFE 内,按两空格缩进闭合提取) */
|
||||
const gsrc = fs.readFileSync(path.join(__dirname, "..", "webui", "graph.js"), "utf-8");
|
||||
function loadGraphFns(names) {
|
||||
|
||||
Reference in New Issue
Block a user