Files
CareerNexus/webui/index.html
T
RoyirinRosmontis 3329c55e06 竞赛演示模式:首屏降噪与预置导览
- demo_mode 配置项(默认开):不弹初始设置,LLM/语义警告横幅降级为顶栏状态点
- 顶栏新增'开始演示'导览(驾驶舱→新岗位发现→Java演化→匹配诊断四步预置路线)
- 图谱默认降噪:场景/行业图层默认关、关系标签默认关(悬停/缩放显示)
- 演化页默认锁定 Java 强样例;数据版本徽标标注'(模拟)';联系方式移入设置页
- start.bat/start.sh 启动脚本
2026-08-12 16:25:37 +08:00

522 lines
30 KiB
HTML
Raw 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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CareerNexus 职联 · 岗位与能力图谱平台</title>
<link rel="stylesheet" href="/static/style.css?v=20260803">
</head>
<body>
<!-- ============ 顶部栏 56px ============ -->
<header class="topbar">
<div class="brand">
<span class="brand-logo">
<svg viewBox="0 0 24 24" fill="none"><circle cx="6" cy="6" r="3"/><circle cx="18" cy="7" r="3"/><circle cx="12" cy="18" r="3"/><path d="M8.5 7.5l7-1M7.5 8.8l3 6.4M16.5 9.6l-3 5.6"/></svg>
</span>
<span class="brand-name">CareerNexus <b>职联</b></span>
</div>
<div class="page-title" id="page-title">数据驾驶舱</div>
<div class="topbar-right">
<div class="global-search">
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/></svg>
<input id="global-search-input" type="text" placeholder="搜索岗位 / 技能 / 场景…" autocomplete="off">
<div id="global-search-drop" class="search-drop hidden"></div>
</div>
<button class="btn btn-primary btn-sm" id="btn-demo-tour">
<svg viewBox="0 0 24 24"><polygon points="6 3 20 12 6 21 6 3"/></svg>
开始演示</button>
<span class="demo-badge" id="demo-badge" title="运行状态(点击查看设置)"><i class="dot"></i><span id="demo-badge-text">演示模式</span></span>
<span class="badge badge-version" id="data-version" title="图谱数据版本">数据版本 —</span>
<button class="icon-btn" id="btn-config" title="系统设置" aria-label="系统设置">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 11-4 0v-.09a1.65 1.65 0 00-1-1.51 1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 11-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 110-4h.09a1.65 1.65 0 001.51-1 1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 112.83-2.83l.06.06a1.65 1.65 0 001.82.33h.01a1.65 1.65 0 001-1.51V3a2 2 0 114 0v.09a1.65 1.65 0 001 1.51h.01a1.65 1.65 0 001.82-.33l.06-.06a2 2 0 112.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82v.01a1.65 1.65 0 001.51 1H21a2 2 0 110 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
</button>
<span class="avatar" title="演示账号">演</span>
</div>
</header>
<!-- ============ 横向导航(顶栏下方) ============ -->
<nav class="topnav" id="topnav">
<button class="nav-item active" data-view="dashboard">
<svg viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="9" rx="1.5"/><rect x="14" y="3" width="7" height="5" rx="1.5"/><rect x="14" y="12" width="7" height="9" rx="1.5"/><rect x="3" y="16" width="7" height="5" rx="1.5"/></svg>
<span>数据驾驶舱</span>
</button>
<button class="nav-item" data-view="graph">
<svg viewBox="0 0 24 24"><circle cx="5" cy="6" r="2.5"/><circle cx="19" cy="6" r="2.5"/><circle cx="12" cy="18" r="2.5"/><path d="M7.3 7.2l9.4-1M6.2 8.3l4.2 7.4M17.8 8.3l-4.2 7.4"/></svg>
<span>全景图谱</span>
</button>
<button class="nav-item" data-view="evolution">
<svg viewBox="0 0 24 24"><path d="M3 17l5.5-5.5 3.5 3.5L20 7"/><path d="M15 7h5v5"/></svg>
<span>岗位演化</span>
</button>
<button class="nav-item" data-view="match">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1" fill="currentColor"/></svg>
<span>人岗匹配</span>
</button>
<button class="nav-item" data-view="path">
<svg viewBox="0 0 24 24"><circle cx="6" cy="19" r="2.5"/><circle cx="18" cy="5" r="2.5"/><path d="M8 17.5C12 14 8 10 12 7.5c1.5-1 3-1.2 4-1.3"/></svg>
<span>学习路径</span>
</button>
</nav>
<!-- 离线回退模式横幅 -->
<div class="offline-banner hidden" id="offline-banner">
<svg viewBox="0 0 24 24"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><path d="M12 9v4M12 17h.01"/></svg>
<span>当前为<b>离线回退模式</b>:未配置 LLM 密钥,抽取与生成使用离线确定性方案。配置星火大模型密钥可启用联网能力。</span>
<button class="banner-btn" id="banner-config">去配置</button>
<button class="banner-close" id="banner-close" title="关闭">×</button>
</div>
<!-- 语义向量模型横幅 -->
<div class="offline-banner hidden" id="embed-banner">
<svg viewBox="0 0 24 24"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><path d="M12 9v4M12 17h.01"/></svg>
<span>语义向量模型<b>未就绪</b>:当前使用词表归一化匹配。下载 BGE-M3 权重并在设置中配置,可启用语义相似匹配增强。</span>
<button class="banner-btn" id="embed-banner-config">去配置</button>
<button class="banner-close" id="embed-banner-close" title="关闭">×</button>
</div>
<div class="shell">
<!-- ============ 主内容区 ============ -->
<main class="content">
<!-- ======== 数据驾驶舱 ======== -->
<section id="view-dashboard" class="view active">
<div class="metric-grid" id="metric-cards"></div>
<div class="grid-2">
<div class="card">
<div class="card-head">
<h3>近 12 个季度演化事件趋势</h3>
<span class="text-weak text-aux">按事件窗口统计</span>
</div>
<div class="card-body"><canvas id="trend-canvas" height="220"></canvas></div>
</div>
<div class="card">
<div class="card-head">
<h3>任务与审核状态</h3>
<button class="btn btn-secondary btn-sm" id="btn-import-jd">导入 JD 数据</button>
</div>
<div class="card-body" id="task-table-wrap">
<table class="table" id="task-table">
<thead><tr><th>类型</th><th>对象</th><th>操作人</th><th>时间</th><th>结果</th></tr></thead>
<tbody></tbody>
</table>
<div class="empty small hidden" id="task-empty">暂无任务记录</div>
</div>
</div>
</div>
<div class="grid-2">
<div class="card">
<div class="card-head"><h3>动态消息流</h3><span class="text-weak text-aux">点击可跳转</span></div>
<div class="card-body feed" id="activity-feed"></div>
</div>
<div class="card">
<div class="card-head"><h3>待审核新岗位候选</h3><span class="badge badge-warning" id="pending-count">0 待办</span></div>
<div class="card-body" id="pending-list"></div>
</div>
</div>
</section>
<!-- ======== 全景图谱工作台 ======== -->
<section id="view-graph" class="view">
<div class="graph-header card">
<div class="graph-title">
<h2>岗位能力图谱工作台</h2>
<span class="tag tag-blue">演示模式</span>
<span class="tag tag-cyan">技能点级</span>
<span class="tag tag-green">审核已开启</span>
</div>
<div class="graph-actions">
<button class="btn btn-secondary" id="btn-save-view">
<svg viewBox="0 0 24 24"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><path d="M17 21v-8H7v8M7 3v5h8"/></svg>
保存视图</button>
<button class="btn btn-secondary" id="btn-share-graph">
<svg viewBox="0 0 24 24"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.6 13.5l6.8 4M15.4 6.5l-6.8 4"/></svg>
分享</button>
<button class="btn btn-primary" id="btn-export-graph">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
导出图谱</button>
</div>
</div>
<div class="graph-body">
<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>
<button class="tool-btn" id="tool-zoom-out" title="缩小"><svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35M8 11h6"/></svg></button>
<button class="tool-btn" id="tool-fit" title="适应画布"><svg viewBox="0 0 24 24"><path d="M8 3H5a2 2 0 00-2 2v3M16 3h3a2 2 0 012 2v3M8 21H5a2 2 0 01-2-2v-3M16 21h3a2 2 0 002-2v-3"/></svg></button>
<span class="tool-sep"></span>
<button class="tool-btn" id="tool-filter" title="筛选"><svg viewBox="0 0 24 24"><path d="M22 3H2l8 9.46V19l4 2v-8.54L22 3z"/></svg></button>
<button class="tool-btn" id="tool-labels" title="关系标签开关"><svg viewBox="0 0 24 24"><path d="M20.59 13.41L11 3H4v7l9.59 9.59a2 2 0 002.82 0l4.18-4.18a2 2 0 000-2.82z"/><circle cx="7.5" cy="7.5" r="1" fill="currentColor"/></svg></button>
<button class="tool-btn" id="tool-relayout" title="一键整理布局"><svg viewBox="0 0 24 24"><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.2 2.2M16.2 16.2l2.2 2.2M5.6 18.4l2.2-2.2M16.2 7.8l2.2-2.2"/></svg></button>
</div>
<!-- 筛选面板 -->
<div class="graph-filter-panel hidden" id="graph-filter-panel">
<div class="fp-head">图谱筛选 <button class="link-btn" id="fp-reset">重置</button></div>
<div class="fp-block">
<div class="fp-label">节点类型</div>
<label class="check"><input type="checkbox" data-ftype="Job" checked><i class="lg-dot job"></i>岗位</label>
<label class="check"><input type="checkbox" data-ftype="Skill" checked><i class="lg-dot skill"></i>技能</label>
<label class="check"><input type="checkbox" data-ftype="Industry"><i class="lg-dot industry"></i>行业</label>
<label class="check"><input type="checkbox" data-ftype="Scenario"><i class="lg-dot scenario"></i>场景</label>
</div>
<div class="fp-block">
<div class="fp-label">连线类型</div>
<label class="check"><input type="checkbox" data-etype="required" checked><i class="lg-line solid"></i>必备技能(116)</label>
<label class="check"><input type="checkbox" data-etype="preferred" checked><i class="lg-line dashed"></i>加分技能(78)</label>
<label class="check"><input type="checkbox" data-etype="applies" checked><i class="lg-line purple"></i>应用场景(45)</label>
<label class="check"><input type="checkbox" data-etype="belongs" checked><i class="lg-line cyan"></i>所属行业(22)</label>
</div>
<div class="fp-block">
<div class="fp-label">岗位类别(技术栈)</div>
<select id="fp-category"><option value="">全部类别</option></select>
</div>
<div class="fp-block">
<div class="fp-label">岗位级别</div>
<select id="fp-level">
<option value="">全部级别</option>
<option value="初级">初级(≤2 年)</option>
<option value="中级">中级(3-5 年)</option>
<option value="高级">高级(&gt;5 年)</option>
</select>
</div>
</div>
<!-- 左下:小地图 + 图例 -->
<div class="graph-minimap">
<canvas id="minimap-canvas" width="180" height="120"></canvas>
<div class="graph-legend">
<span><i class="lg-dot job"></i>岗位</span>
<span><i class="lg-sq skill"></i>技能</span>
<span><i class="lg-dot scenario"></i>场景</span>
<span><i class="lg-dot pending"></i>待审核</span>
<span><i class="lg-line solid"></i>必备</span>
<span><i class="lg-line dashed"></i>加分</span>
</div>
</div>
<!-- 右下:智能建议 -->
<div class="graph-suggest card" id="graph-suggest">
<div class="gs-head">
<svg viewBox="0 0 24 24"><path d="M12 2a7 7 0 00-4 12.7V17a1 1 0 001 1h6a1 1 0 001-1v-2.3A7 7 0 0012 2z"/><path d="M9 21h6"/></svg>
智能建议
<button class="icon-btn sm" id="gs-toggle" title="收起/展开"><svg viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg></button>
</div>
<div class="gs-list" id="gs-list"></div>
</div>
<!-- 底部:演化逻辑链 -->
<div class="graph-chain" id="graph-chain">
<div class="gc-head">演化逻辑链 <span class="text-weak text-aux" id="gc-job-name">选择岗位节点后展示能力演化路径</span></div>
<div class="gc-chips" id="gc-chips"><span class="text-weak text-aux">—</span></div>
</div>
<div class="graph-loading" id="graph-loading">
<div class="skeleton-graph"></div>
<div class="text-weak">图谱布局计算中…</div>
</div>
</div>
<!-- 右侧详情面板 320px -->
<aside class="graph-detail card" id="graph-detail">
<div class="detail-empty" id="detail-empty">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8v5M12 16h.01"/></svg>
<p>点击画布中的节点<br>查看详情、证据与操作</p>
<p class="text-aux text-weak">支持拖拽平移 / 滚轮缩放 / 点击选中</p>
</div>
<div class="detail-body hidden" id="detail-body"></div>
</aside>
</div>
</section>
<!-- ======== 岗位演化 ======== -->
<section id="view-evolution" class="view">
<div class="card">
<div class="card-head">
<h3>岗位能力演化分析</h3>
<div class="evo-controls">
<select id="evo-job-select" class="select"></select>
<div class="seg" id="evo-gran">
<button data-gran="month">月</button>
<button data-gran="quarter" class="active">季</button>
<button data-gran="year">年</button>
</div>
</div>
</div>
<div class="card-body">
<!-- 全部岗位演化热力图 -->
<div class="evo-chart-title text-aux text-secondary">全部岗位演化热力图(每格 = 该季度演化事件数,点击行切换岗位)</div>
<div class="evo-heatmap" id="evo-heatmap"><div class="empty small">加载中…</div></div>
<!-- 统计条 -->
<div class="evo-stats" id="evo-stats"></div>
<!-- 时点对比器 -->
<div class="evo-compare">
<div class="evo-compare-head">
<span class="evo-chart-title">能力画像时点对比</span>
<div class="evo-dates">
<input type="date" id="evo-date-a" class="input">
<span class="text-weak">→</span>
<input type="date" id="evo-date-b" class="input">
<button class="btn btn-secondary btn-sm" id="evo-compare-btn">对比</button>
</div>
</div>
<div class="evo-diff" id="evo-diff"></div>
</div>
<!-- 变更类型构成(堆叠柱状) -->
<div class="evo-chart-title text-aux text-secondary">变更类型构成(每季度 新增/增强/减弱/退出)</div>
<div class="evo-heat-wrap"><canvas id="evo-stack-canvas"></canvas></div>
<div class="stack-legend" id="stack-legend"></div>
<!-- 技能生命周期甘特图 -->
<div class="evo-chart-title text-aux text-secondary">技能生命周期(横条 = 技能在画像中的生效区间,圆点 = 变更事件)</div>
<div class="evo-gantt" id="evo-gantt"></div>
<!-- 热度走势(限高) -->
<div class="evo-chart-title text-aux text-secondary">技能热度走势(活跃技能关系数)</div>
<div class="evo-heat-wrap"><canvas id="evo-heat-canvas"></canvas></div>
<!-- 事件时间轴 -->
<div class="evo-chart-title text-aux text-secondary">演化事件时间轴(点击查看证据 JD)</div>
<div class="timeline" id="evo-timeline"></div>
</div>
</div>
</section>
<!-- ======== 人岗匹配(三步向导 + 批量匹配) ======== -->
<section id="view-match" class="view">
<div class="card">
<div class="card-body">
<div class="match-tabs">
<button class="match-tab active" data-mtab="single">单人诊断</button>
<button class="match-tab" data-mtab="batch">批量匹配</button>
</div>
<div id="match-single">
<div class="steps" id="match-steps">
<div class="step active" data-step="1"><i>1</i><span>简历录入</span></div>
<div class="step" data-step="2"><i>2</i><span>解析确认</span></div>
<div class="step" data-step="3"><i>3</i><span>诊断结果</span></div>
</div>
<!-- 第 1 步 -->
<div class="match-pane" id="match-pane-1">
<div class="form-row">
<label class="form-label">目标岗位</label>
<select id="match-job-select" class="select w-320"></select>
</div>
<div class="form-row">
<label class="form-label">简历文本</label>
<textarea id="resume-text" class="textarea" rows="10" placeholder="粘贴简历全文(将自动脱敏与技能抽取),或点击下方按钮上传 / 载入示例简历"></textarea>
</div>
<div class="form-actions">
<button class="btn btn-secondary" id="btn-load-sample">载入示例简历</button>
<label class="btn btn-secondary" for="resume-file">上传简历文件</label>
<input type="file" id="resume-file" accept=".txt,.md,.docx,.pdf" class="hidden">
<span class="text-aux text-weak">支持 .txt / .docx / .pdf,≤5MB</span>
<button class="btn btn-primary" id="btn-parse-resume">解析简历</button>
</div>
</div>
<!-- 第 2 步 -->
<div class="match-pane hidden" id="match-pane-2">
<div class="parse-summary" id="parse-summary"></div>
<div class="form-row">
<label class="form-label">识别技能(可增删)</label>
<div class="chip-editor" id="skill-chip-editor"></div>
<div class="chip-add">
<input id="skill-add-input" class="input" placeholder="输入技能后回车添加,支持逗号分隔,如 Kubernetes, Docker">
<button class="btn btn-secondary btn-sm" id="btn-skill-add">添加</button>
</div>
</div>
<div class="form-row">
<label class="form-label">工作年限</label>
<input id="parse-years" type="number" min="0" max="40" step="0.5" class="input w-120">
</div>
<div class="form-actions">
<button class="btn btn-secondary" id="btn-back-1">上一步</button>
<button class="btn btn-primary" id="btn-run-match">开始诊断</button>
</div>
</div>
<!-- 第 3 步 -->
<div class="match-pane hidden" id="match-pane-3">
<div class="match-step3-grid">
<!-- 左:简历原文 + 技能高亮 -->
<div class="resume-viewer">
<div class="rv-head">
<span class="rv-title">结构化简历(已脱敏)</span>
<span class="rv-legend">
<span class="hl hl-req">必备</span>
<span class="hl hl-pref">加分</span>
<span class="hl hl-other">其他技能</span>
<button class="btn btn-secondary btn-sm" id="btn-export-resume">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
导出 DOCX</button>
</span>
</div>
<div class="rv-body" id="resume-highlight">
<div class="empty"><p>解析简历后此处显示规范简历模板</p></div>
</div>
</div>
<!-- 右:诊断结果 -->
<div class="match-step3-right">
<div class="match-result-grid">
<div class="mrg-left">
<div class="gauge-wrap"><canvas id="gauge-canvas" width="220" height="150"></canvas>
<div class="gauge-text"><b id="gauge-score">0</b><span>综合匹配分</span></div>
</div>
<div class="match-verdict" id="match-verdict"></div>
<canvas id="radar-canvas" width="340" height="280"></canvas>
</div>
<div class="mrg-right">
<div class="hit-miss">
<div class="hm-col">
<div class="hm-title ok"><svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg>已命中技能</div>
<div class="hm-chips" id="matched-chips"></div>
</div>
<div class="hm-col">
<div class="hm-title bad"><svg viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></svg>缺失技能</div>
<div class="hm-chips" id="missing-chips"></div>
</div>
</div>
<div class="transfer-block">
<div class="hm-title info"><svg viewBox="0 0 24 24"><path d="M17 3l4 4-4 4M21 7H9a4 4 0 00-4 4v1M7 21l-4-4 4-4M3 17h12a4 4 0 004-4v-1"/></svg>可迁移技能</div>
<div class="hm-chips" id="transfer-chips"></div>
</div>
<div class="transfer-block hidden" id="irrelevant-block">
<div class="hm-title weak">与目标岗位无关的技能(不计入评分)</div>
<div class="hm-chips" id="irrelevant-chips"></div>
</div>
</div>
</div>
<div class="rec-jobs">
<div class="hm-title info">推荐岗位(按匹配分排序)</div>
<div class="rec-job-cards" id="rec-job-cards"></div>
</div>
<div class="form-actions">
<button class="btn btn-secondary" id="btn-back-2">重新录入</button>
<button class="btn btn-primary" id="btn-goto-path">生成学习路径</button>
</div>
</div>
</div>
</div>
</div><!-- /match-single -->
<!-- ======== 批量匹配 ======== -->
<div id="match-batch" class="hidden">
<div class="form-row">
<label class="form-label">目标岗位</label>
<select id="batch-job-select" class="select w-320"></select>
</div>
<div class="dropzone" id="batch-drop">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>
<p><b>拖拽简历文件或文件夹到此处</b>,或:</p>
<div class="dropzone-btns">
<label class="btn btn-secondary" for="batch-files">选择文件(可多选)</label>
<label class="btn btn-secondary" for="batch-dir">选择文件夹</label>
</div>
<input type="file" id="batch-files" accept=".txt,.md,.docx,.pdf" multiple class="hidden">
<input type="file" id="batch-dir" webkitdirectory class="hidden">
<p class="text-aux text-weak">支持 .txt/.docx/.pdf · 单批 ≤50 份 · 单份 ≤5MB · 解析自动脱敏</p>
</div>
<div class="batch-filelist" id="batch-filelist"></div>
<div class="form-actions">
<button class="btn btn-primary" id="btn-batch-run">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1" fill="currentColor"/></svg>
开始批量匹配</button>
<button class="btn btn-secondary hidden" id="btn-batch-export">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
导出结果 CSV</button>
</div>
<div id="batch-result"></div>
</div>
</div>
</div>
</section>
<!-- ======== 学习路径 ======== -->
<section id="view-path" class="view">
<div class="path-layout">
<!-- 左栏:路径配置 -->
<div class="card path-side">
<div class="card-head"><h3>路径配置</h3></div>
<div class="card-body">
<div class="form-item">
<label class="form-label">目标岗位</label>
<select id="path-job-select" class="select w-full"></select>
</div>
<div class="form-item">
<label class="form-label">岗位能力清单<span class="form-label-tip">点击标记为「已掌握」</span></label>
<div class="skill-pool" id="path-skill-pool">
<div class="pool-loading">选择岗位后加载技能清单…</div>
</div>
</div>
<div class="form-item">
<label class="form-label">学习周期</label>
<div class="seg" id="path-weeks">
<button class="seg-item" data-weeks="4">4 周</button>
<button class="seg-item active" data-weeks="8">8 周</button>
<button class="seg-item" data-weeks="12">12 周</button>
</div>
</div>
<div class="path-summary" id="path-summary">
<div class="path-summary-row"><span>已掌握</span><b id="ps-known">0 项</b></div>
<div class="path-summary-row"><span>待补齐</span><b id="ps-gap">—</b></div>
<div class="progress"><div class="progress-bar" id="ps-bar" style="width:0%"></div></div>
<div class="path-summary-tip" id="ps-tip">覆盖率 0%</div>
</div>
<button class="btn btn-primary btn-block" id="btn-path">
<svg viewBox="0 0 24 24"><circle cx="6" cy="19" r="2.5"/><circle cx="18" cy="5" r="2.5"/><path d="M8 17.5C12 14 8 10 12 7.5c1.5-1 3-1.2 4-1.3"/></svg>
生成学习路径</button>
</div>
</div>
<!-- 右栏:路径结果 -->
<div class="card path-main">
<div class="card-head">
<h3>分阶段学习路线</h3>
<button class="btn btn-secondary btn-sm hidden" id="btn-export-path">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
导出文本</button>
</div>
<div class="card-body">
<div class="path-overview hidden" id="path-overview"></div>
<div class="path-flow" id="path-result">
<div class="empty">
<svg viewBox="0 0 24 24"><circle cx="6" cy="19" r="2.5"/><circle cx="18" cy="5" r="2.5"/><path d="M8 17.5C12 14 8 10 12 7.5c1.5-1 3-1.2 4-1.3"/></svg>
<p>在左侧选择目标岗位并标记已掌握技能<br>点击「生成学习路径」查看分阶段路线</p>
</div>
</div>
</div>
</div>
</div>
</section>
<footer class="app-footer" id="app-footer">CareerNexus 职联 · 演示模式 · 数据为模拟生成</footer>
</main>
</div>
<!-- 通用弹窗容器 -->
<div class="modal-mask hidden" id="modal-mask">
<div class="modal" id="modal-box" role="dialog" aria-modal="true">
<div class="modal-head"><h3 id="modal-title"></h3>
<button class="icon-btn" id="modal-close" title="关闭"><svg viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></svg></button>
</div>
<div class="modal-body" id="modal-body"></div>
<div class="modal-foot hidden" id="modal-foot"></div>
</div>
</div>
<div class="toast-wrap" id="toast-wrap"></div>
<script src="/static/app.js?v=20260803"></script>
<script src="/static/graph.js?v=20260803"></script>
</body>
</html>