竞赛演示模式:首屏降噪与预置导览
- demo_mode 配置项(默认开):不弹初始设置,LLM/语义警告横幅降级为顶栏状态点 - 顶栏新增'开始演示'导览(驾驶舱→新岗位发现→Java演化→匹配诊断四步预置路线) - 图谱默认降噪:场景/行业图层默认关、关系标签默认关(悬停/缩放显示) - 演化页默认锁定 Java 强样例;数据版本徽标标注'(模拟)';联系方式移入设置页 - start.bat/start.sh 启动脚本
This commit is contained in:
+79
-10
@@ -255,7 +255,7 @@ async function loadDashboard() {
|
||||
CN.graphData = graph;
|
||||
const s = health.graph || {};
|
||||
updateBanners(health);
|
||||
$("#data-version").textContent = `数据版本 v${(graph.events.at(-1) || {}).window || "demo"} · ${s.nodes ?? 0} 节点`;
|
||||
$("#data-version").textContent = `数据更新至 ${(graph.events.at(-1) || {}).window || "2026Q3"}(模拟)`;
|
||||
|
||||
/* 指标卡 */
|
||||
const series = quarterlyCounts(graph.events || []);
|
||||
@@ -1421,21 +1421,31 @@ $("#btn-export-path").onclick = () => {
|
||||
toast("已导出文本", "success");
|
||||
};
|
||||
|
||||
/* ================= 系统设置 / 状态横幅 ================= */
|
||||
let lastHealth = null; // 最近一次 health 响应(横幅状态源)
|
||||
/* ================= 系统设置 / 状态横幅 / 演示模式 ================= */
|
||||
let lastHealth = null; // 最近一次 health 响应(横幅与状态徽标的数据源)
|
||||
function updateBanners(health) {
|
||||
if (health) lastHealth = health;
|
||||
const h = lastHealth || {};
|
||||
const showOffline = !!h.offline_fallback
|
||||
&& sessionStorage.getItem("cn_banner_dismissed") !== "1";
|
||||
const demo = h.demo_mode !== false;
|
||||
const embed = h.embed || {};
|
||||
const showEmbed = !embed.ready
|
||||
const degraded = !!h.offline_fallback || !embed.ready;
|
||||
// 演示模式:警告横幅降级为顶栏状态徽标(首屏不出现负面字眼)
|
||||
const showOffline = !demo && !!h.offline_fallback
|
||||
&& sessionStorage.getItem("cn_banner_dismissed") !== "1";
|
||||
const showEmbed = !demo && !embed.ready
|
||||
&& sessionStorage.getItem("cn_banner2_dismissed") !== "1";
|
||||
$("#offline-banner").classList.toggle("hidden", !showOffline);
|
||||
$("#embed-banner").classList.toggle("hidden", !showEmbed);
|
||||
const n = (showOffline ? 1 : 0) + (showEmbed ? 1 : 0);
|
||||
document.body.classList.toggle("has-banner", n === 1);
|
||||
document.body.classList.toggle("has-banner2", n === 2);
|
||||
// 顶栏状态徽标:绿=全就绪,黄=离线回退/语义未就绪
|
||||
const badge = $("#demo-badge");
|
||||
badge.classList.toggle("warn", degraded);
|
||||
$("#demo-badge-text").textContent = degraded ? "离线演示" : (demo ? "演示模式" : "在线");
|
||||
badge.title = degraded
|
||||
? "离线回退中(LLM/语义模型未配置),点击查看设置"
|
||||
: "全部通道就绪,点击查看设置";
|
||||
}
|
||||
function dismissBanner(id, key) {
|
||||
sessionStorage.setItem(key, "1");
|
||||
@@ -1445,6 +1455,44 @@ $("#banner-close").onclick = () => dismissBanner("offline-banner", "cn_banner_di
|
||||
$("#embed-banner-close").onclick = () => dismissBanner("embed-banner", "cn_banner2_dismissed");
|
||||
$("#banner-config").onclick = () => openConfigModal(false);
|
||||
$("#embed-banner-config").onclick = () => openConfigModal(false);
|
||||
$("#demo-badge").onclick = () => openConfigModal(false);
|
||||
|
||||
/* ================= 演示导览(预置路线,一键串起完整闭环) ================= */
|
||||
const DEMO_STEPS = [
|
||||
{ title: "① 数据驾驶舱:多源数据总览", desc: "JD 规模、岗位/技能/事件总量、演化趋势、清洗与审核状态",
|
||||
go: () => switchView("dashboard") },
|
||||
{ title: "② 新岗位发现:候选→审核→入图", desc: "图谱定位「大模型应用工程师」:四指标证据、人工确认、证据 JD 回查",
|
||||
go: () => { switchView("graph"); if (CN.gotoNode) CN.gotoNode("job_大模型应用工程师"); } },
|
||||
{ title: "③ 既有岗位演化:Java 编年史", desc: "时点对比、变更堆叠图、生命周期甘特、事件证据链",
|
||||
go: () => {
|
||||
switchView("evolution");
|
||||
$("#evo-job-select").value = "job_Java开发工程师";
|
||||
$("#evo-job-select").dispatchEvent(new Event("change"));
|
||||
} },
|
||||
{ title: "④ 人岗匹配诊断 + 学习路径", desc: "载入示例简历 → 确认解析 → 总分/四维/差距清单 → 一键生成学习路径",
|
||||
go: () => {
|
||||
switchView("match");
|
||||
$$(".match-tab").forEach((x) => x.classList.toggle("active", x.dataset.mtab === "single"));
|
||||
$("#match-single").classList.remove("hidden");
|
||||
$("#match-batch").classList.add("hidden");
|
||||
$("#btn-load-sample").click();
|
||||
} },
|
||||
];
|
||||
$("#btn-demo-tour").onclick = () => {
|
||||
openModal("演示导览(预置路线)", `
|
||||
<div class="tour-intro">按预置路线依次演示完整闭环,每步点击「前往」自动跳转到位:</div>
|
||||
${DEMO_STEPS.map((s, i) => `
|
||||
<div class="tour-step">
|
||||
<div class="tour-step-body">
|
||||
<div class="tour-step-title">${esc(s.title)}</div>
|
||||
<div class="tour-step-desc">${esc(s.desc)}</div>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm" data-step="${i}">前往</button>
|
||||
</div>`).join("")}`);
|
||||
$$("#modal-body [data-step]").forEach((b) => {
|
||||
b.onclick = () => { closeModal(); DEMO_STEPS[+b.dataset.step].go(); };
|
||||
});
|
||||
};
|
||||
|
||||
function embedStatusHtml(emb) {
|
||||
if (emb.ready) {
|
||||
@@ -1506,6 +1554,13 @@ async function openConfigModal(needToken, firstRun) {
|
||||
<button class="btn btn-secondary btn-sm" id="cfg-token-show">显示</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label class="form-label">演示模式<span class="form-label-tip">开启后不弹初始设置、警告降级为顶栏状态点</span></label>
|
||||
<div class="seg" id="cfg-demo">
|
||||
<button class="seg-item ${cfg.demo_mode !== false ? "active" : ""}" data-demo="1">开启(评审演示)</button>
|
||||
<button class="seg-item ${cfg.demo_mode === false ? "active" : ""}" data-demo="0">关闭(开发调试)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cfg-sec">
|
||||
<div class="cfg-sec-title">LLM API(星火 / OpenAI 兼容,保存即热生效)</div>
|
||||
@@ -1584,7 +1639,8 @@ async function openConfigModal(needToken, firstRun) {
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
</div>
|
||||
<div class="cfg-foot-note">如遇问题,请联系 support@rrrosmontis.icu</div>`,
|
||||
`${firstRun ? `<button class="btn btn-secondary" id="cfg-skip">跳过,使用离线演示模式</button>` : ""}
|
||||
<button class="btn btn-secondary" id="cfg-save-restart">保存并重启</button>
|
||||
<button class="btn btn-primary" id="cfg-save">保存设置</button>`);
|
||||
@@ -1598,6 +1654,14 @@ async function openConfigModal(needToken, firstRun) {
|
||||
};
|
||||
});
|
||||
let embedProvider = cfg.embed.provider === "api" ? "api" : "local";
|
||||
let demoMode = cfg.demo_mode !== false;
|
||||
$$("#cfg-demo .seg-item").forEach((b) => {
|
||||
b.onclick = () => {
|
||||
$$("#cfg-demo .seg-item").forEach((x) => x.classList.remove("active"));
|
||||
b.classList.add("active");
|
||||
demoMode = b.dataset.demo === "1";
|
||||
};
|
||||
});
|
||||
$$("#cfg-embed-provider .seg-item").forEach((b) => {
|
||||
b.onclick = () => {
|
||||
$$("#cfg-embed-provider .seg-item").forEach((x) => x.classList.remove("active"));
|
||||
@@ -1629,6 +1693,7 @@ async function openConfigModal(needToken, firstRun) {
|
||||
embed_model_path: $("#cfg-embed-path").value.trim(),
|
||||
embed_api_base_url: $("#cfg-embed-base").value.trim(),
|
||||
embed_api_model: $("#cfg-embed-model").value.trim(),
|
||||
demo_mode: demoMode,
|
||||
guided: true, // 保存即视为完成初始设置
|
||||
};
|
||||
const tok = $("#cfg-token").value;
|
||||
@@ -1643,7 +1708,7 @@ async function openConfigModal(needToken, firstRun) {
|
||||
else localStorage.removeItem("cn_access_token");
|
||||
}
|
||||
lastHealth = Object.assign({}, lastHealth, {
|
||||
offline_fallback: saved.offline_fallback, embed: saved.embed,
|
||||
offline_fallback: saved.offline_fallback, embed: saved.embed, demo_mode: saved.demo_mode,
|
||||
});
|
||||
updateBanners();
|
||||
return saved;
|
||||
@@ -1716,6 +1781,8 @@ window.addEventListener("resize", () => {
|
||||
CN.jobs = await api("/api/jobs");
|
||||
const opts = CN.jobs.map((j) => `<option value="${esc(j.job_id)}">${esc(j.title)}</option>`).join("");
|
||||
["#evo-job-select", "#match-job-select", "#path-job-select", "#batch-job-select"].forEach((s) => { $(s).innerHTML = opts; });
|
||||
$("#evo-job-select").value = "job_Java开发工程师"; // 演化页默认锁定最强样例
|
||||
$("#match-job-select").value = "job_Java开发工程师";
|
||||
await loadDashboard(); // 先加载图谱缓存(演化页甘特图依赖)
|
||||
await loadEvolution();
|
||||
renderEvoHeatmap();
|
||||
@@ -1723,8 +1790,10 @@ window.addEventListener("resize", () => {
|
||||
const hash = new URLSearchParams(location.hash.slice(1));
|
||||
if (hash.get("view")) switchView(hash.get("view"));
|
||||
if (hash.get("node") && CN.gotoNode) CN.gotoNode(hash.get("node"));
|
||||
/* 首次使用:引导完成基础设置(完成标记记录在服务端 config.json) */
|
||||
if (!lastHealth || !lastHealth.guided) openConfigModal(false, true);
|
||||
/* 首次使用:演示模式下不弹初始设置(评审口径),非演示模式才引导 */
|
||||
if ((!lastHealth || !lastHealth.guided) && lastHealth && lastHealth.demo_mode === false) {
|
||||
openConfigModal(false, true);
|
||||
}
|
||||
} catch (e) {
|
||||
toast("后端连接失败:" + e.message, "error");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user