Files
CareerNexus/webui/index.html
T

625 lines
37 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=20260819">
</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="topbar-left-actions">
<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>
</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>
<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="card cmd-hero">
<div class="cmd-hero-main">
<h2 id="cmd-title">人才能力变化概览</h2>
<div class="cmd-sub" id="cmd-sub">正在汇总本期变化…</div>
<div class="cmd-scale text-weak" id="cmd-scale"></div>
</div>
<div class="cmd-hero-actions">
<button class="btn btn-primary btn-sm" id="btn-cmd-report">查看本期报告</button>
<button class="btn btn-secondary btn-sm" id="btn-open-review-desk">候选审核台</button>
<button class="btn btn-secondary btn-sm" id="btn-import-jd">导入 JD 数据</button>
</div>
</div>
<div class="metric-grid cmd-kpis" id="metric-cards"></div>
<div class="grid-2">
<div class="card">
<div class="card-head">
<h3>近 8 季度变化事件流(全岗位)</h3>
<span class="text-weak text-aux">点击柱段进入该季度演化详情</span>
</div>
<div class="card-body">
<canvas id="trend-canvas" height="220"></canvas>
<div class="stack-legend" id="cmd-stack-legend"></div>
</div>
</div>
<div class="card">
<div class="card-head">
<h3>数据可信度</h3>
<span class="text-weak text-aux">多源交叉验证口径</span>
</div>
<div class="card-body" id="trust-card"><div class="empty small">统计中…</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="cmd-head-right">
<span class="badge badge-warning" id="pending-count">0 待办</span>
<button class="btn btn-text btn-sm" id="btn-open-review-desk-2">进入审核台</button>
</span>
</div>
<div class="card-body" id="pending-list"></div>
</div>
</div>
<div class="card cmd-ledger">
<div class="card-head">
<h3>任务与审核台账</h3>
<button class="btn btn-text btn-sm" id="btn-audit-toggle">展开台账</button>
</div>
<div class="card-body hidden" 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>
</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-goto-evo">
<svg viewBox="0 0 24 24"><path d="M3 17l5.5-5.5 3.5 3.5L20 7"/><path d="M15 7h5v5"/></svg>
查看演化</button>
<button class="btn btn-secondary" id="btn-goto-evidence">
<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>
查看证据</button>
<button class="btn btn-primary" id="btn-goto-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>
开始匹配</button>
<div class="more-wrap">
<button class="btn btn-secondary" id="btn-more-menu">更多 ▾</button>
<div class="more-menu hidden" id="more-menu">
<button class="more-item" 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="more-item" 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="more-item" 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>
导出图谱 PNG</button>
</div>
</div>
</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-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>
<!-- 系统发现(原画布浮层「智能建议」移入此处,含采纳/忽略) -->
<details class="sys-findings" id="sys-findings" open>
<summary>系统发现(智能建议)</summary>
<div class="gs-list" id="gs-list"></div>
</details>
</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">
<!-- 二级 Tab:岗位报告(默认) / 全岗位扫描(热力图) -->
<div class="match-tabs" id="evo-tabs">
<button class="match-tab active" data-evotab="report">岗位报告</button>
<button class="match-tab" data-evotab="scan">全岗位扫描</button>
</div>
<div id="evo-tab-scan" class="hidden">
<!-- 全岗位扫描:近 12 季度各岗位变化量横条排行(比热力图直观) -->
<div class="evo-chart-title text-aux text-secondary">全岗位扫描 · 近 12 季度变化量排行(点击行进入该岗位报告)</div>
<div class="evo-chart-desc">怎么看:每行一个岗位,横条长度=近 12 个季度的能力变化总量;绿色段=新增的技能,红色段=退出的技能。条越长说明这个岗位的能力要求变化越剧烈。</div>
<div class="evo-scan" id="evo-scan"><div class="empty small">加载中…</div></div>
</div>
<div id="evo-tab-report">
<!-- 统计条 -->
<div class="evo-stats" id="evo-stats"></div>
<!-- 本期结论(最近两个季度的新增/增强/减弱/退出) -->
<div class="evo-chart-title text-aux text-secondary">本期结论(最近两个季度)</div>
<div class="evo-chart-desc">是什么:系统根据近两个季度的招聘 JD 自动归纳出的能力变化结论。新增=以前不要求、现在要求;增强=要求变重;减弱=要求变少;退出=不再要求。每条结论都可追溯到原始 JD 证据。</div>
<div class="evo-summary" id="evo-summary"><div class="empty small">加载中…</div></div>
<!-- 能力更替流:旧核心 → 持续能力 → 新核心 -->
<div class="evo-chart-title text-aux text-secondary">能力更替流(旧核心 → 持续能力 → 新核心)</div>
<div class="evo-chart-desc">是什么:把岗位技能按演化结果分三列——左边是已退出的旧要求,中间是一直有效的持续能力,右边是新进入的要求。用于回答"这个岗位正在从什么技术栈转向什么技术栈"。</div>
<div class="evo-replace" id="evo-replace"></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-chart-desc">怎么用:选择两个日期,系统回放该岗位在两个时点的完整能力画像并求差集——红色=左边时点有、后来退出;绿色=右边时点新增;灰色=一直保留(带↑表示从加分升级为必备)。</div>
<div class="evo-diff" id="evo-diff"></div>
</div>
<!-- 变更类型构成(堆叠柱状) -->
<div class="evo-chart-title text-aux text-secondary">变更类型构成(每季度 新增/增强/减弱/退出)</div>
<div class="evo-chart-desc">怎么看:每根柱子是一个季度,四种颜色堆叠表示该季度四类变化的数量。柱子变高=该岗位在加速变化;绿色段厚=能力在扩张;橙红段厚=能力在收缩。用于判断岗位所处阶段(扩张期/调整期/稳定期)。</div>
<div class="evo-heat-wrap"><canvas id="evo-stack-canvas"></canvas></div>
<div class="stack-legend" id="stack-legend"></div>
<!-- 热度走势(限高,默认近 8 个季度,纵轴为活跃技能关系数) -->
<div class="evo-chart-title text-aux text-secondary evo-heat-head">
<span>技能热度走势(纵轴:活跃技能关系数)</span>
<span class="seg seg-sm" id="evo-heat-range">
<button data-range="8" class="active">近 8 季</button>
<button data-range="all">全部</button>
</span>
</div>
<div class="evo-chart-desc">是什么:该岗位每个季度实际生效的技能总数(按岗位-技能关系的生效区间统计)。曲线上升=岗位要求在扩充,下降=在精简,平直=稳定。可切换近 8 季 / 全部历史。</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="evo-chart-desc">怎么用:每一次能力变化的明细卡片,含变化说明、前后需求指数、支撑 JD 数量。点击任意卡片可查看支撑该结论的原始 JD 证据(来源平台、企业、发布时间、原文片段)。</div>
<div class="timeline" id="evo-timeline"></div>
</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 步:60/40 布局(左:简历输入;右:目标岗位信息卡) -->
<div class="match-pane" id="match-pane-1">
<div class="match-input-grid">
<div class="match-input-left">
<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>
<div class="match-input-right">
<div class="job-info-card" id="match-job-info">
<div class="text-weak text-aux">岗位信息加载中…</div>
</div>
</div>
</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>
<div class="match-advice" id="match-advice"></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>
<button class="btn btn-primary btn-sm" id="path-goto-match">去完成人岗诊断</button>
</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>
<!-- 候选岗位审核台(右侧抽屉式二级界面) -->
<div class="drawer-mask hidden" id="review-desk-mask">
<aside class="drawer" id="review-desk" role="dialog" aria-modal="true">
<div class="drawer-head">
<h3>候选岗位审核台</h3>
<span class="text-weak text-aux">发现 → 证据 → 人工决策 → 自动入图</span>
<button class="icon-btn" id="review-desk-close" title="关闭"><svg viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12"/></svg></button>
</div>
<div class="drawer-body" id="review-desk-body"></div>
</aside>
</div>
<!-- 评委演示导览浮动条(右上角固定) -->
<div class="tour-bar hidden" id="tour-bar">
<span class="tour-prog" id="tour-prog">演示 1/6</span>
<span class="tour-text">
<b id="tour-title"></b>
<span class="tour-desc" id="tour-desc"></span>
</span>
<button class="btn btn-secondary btn-sm" id="tour-prev">上一步</button>
<button class="btn btn-primary btn-sm" id="tour-next">下一步</button>
<button class="btn btn-text btn-sm" id="tour-exit">退出</button>
</div>
<script src="/static/app.js?v=20260819"></script>
<script src="/static/graph.js?v=20260819"></script>
</body>
</html>