update: figure
This commit is contained in:
894
docs/competition-book/figures/multi-agent-flow.html
Normal file
894
docs/competition-book/figures/multi-agent-flow.html
Normal file
@@ -0,0 +1,894 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>多智能体协同流程图</title>
|
||||
<style>
|
||||
:root {
|
||||
--text: #1f2937;
|
||||
--muted: #5b6472;
|
||||
--panel: #ffffff;
|
||||
--line: #d8e1ef;
|
||||
--blue: #2563eb;
|
||||
--blue-soft: #dbeafe;
|
||||
--green: #16a34a;
|
||||
--green-soft: #dcfce7;
|
||||
--amber: #d97706;
|
||||
--amber-soft: #fef3c7;
|
||||
--red: #dc2626;
|
||||
--red-soft: #fee2e2;
|
||||
--purple: #7c3aed;
|
||||
--purple-soft: #ede9fe;
|
||||
--slate: #475569;
|
||||
--slate-soft: #e2e8f0;
|
||||
--shadow: 0 14px 30px rgba(15, 23, 42, 0.08);
|
||||
--diagram-scale: 1;
|
||||
--diagram-width: 1600px;
|
||||
--diagram-height: 1060px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
|
||||
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
color: var(--text);
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(219, 234, 254, 0.9), transparent 28%),
|
||||
linear-gradient(180deg, #eef4ff 0%, #f7faff 38%, #f5f7fb 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: 1920px;
|
||||
margin: 0 auto;
|
||||
padding: 18px 24px 30px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: grid;
|
||||
grid-template-columns: 1.55fr 1fr;
|
||||
gap: 18px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.hero,
|
||||
.summary,
|
||||
.board,
|
||||
.legend-panel {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||
border-radius: 22px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 22px 24px;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 28px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.summary {
|
||||
padding: 18px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.metric {
|
||||
border: 1px solid #e7eef8;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%);
|
||||
padding: 14px 16px;
|
||||
}
|
||||
|
||||
.metric .num {
|
||||
font-size: 28px;
|
||||
font-weight: 800;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.metric .label {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.board {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.board-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.toolbar-desc {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.toolbar-actions button {
|
||||
appearance: none;
|
||||
border: 1px solid #d8e2f0;
|
||||
background: #fff;
|
||||
border-radius: 999px;
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
transition: 0.2s ease;
|
||||
}
|
||||
|
||||
.toolbar-actions button:hover {
|
||||
background: #eff6ff;
|
||||
border-color: #93c5fd;
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.toolbar-actions button.active {
|
||||
background: #eff6ff;
|
||||
border-color: var(--blue);
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.scale-indicator {
|
||||
min-width: 78px;
|
||||
text-align: right;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.board-shell {
|
||||
min-height: calc(1060px * var(--diagram-scale) + 24px);
|
||||
border-radius: 24px;
|
||||
overflow: auto;
|
||||
border: 1px solid #dbe4f0;
|
||||
background: linear-gradient(180deg, rgba(251, 253, 255, 0.96), rgba(245, 248, 252, 0.98));
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.board-viewport {
|
||||
width: 100%;
|
||||
min-width: fit-content;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.board-inner {
|
||||
position: relative;
|
||||
width: var(--diagram-width);
|
||||
height: var(--diagram-height);
|
||||
transform-origin: top center;
|
||||
transform: scale(var(--diagram-scale));
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||
border-radius: 28px;
|
||||
box-shadow: var(--shadow);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.lane {
|
||||
position: absolute;
|
||||
left: 22px;
|
||||
right: 22px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.18);
|
||||
padding: 18px 18px 14px;
|
||||
}
|
||||
|
||||
.lane-title {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 18px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
color: #243244;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid rgba(148, 163, 184, 0.22);
|
||||
}
|
||||
|
||||
.lane-subtitle {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 18px;
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lane-input { top: 86px; height: 118px; background: linear-gradient(90deg, #eef6ff, #f8fbff); }
|
||||
.lane-sense { top: 224px; height: 192px; background: linear-gradient(90deg, #f0f9ff, #f8fbff); }
|
||||
.lane-reason { top: 436px; height: 234px; background: linear-gradient(90deg, #f7f9ff, #fbfcff); }
|
||||
.lane-review { top: 690px; height: 176px; background: linear-gradient(90deg, #f9fafb, #fcfdff); }
|
||||
.lane-output { top: 886px; height: 150px; background: linear-gradient(90deg, #f6f3ff, #fbfaff); }
|
||||
|
||||
.node {
|
||||
position: absolute;
|
||||
width: 248px;
|
||||
min-height: 120px;
|
||||
border-radius: 20px;
|
||||
background: var(--panel);
|
||||
border: 1px solid #d9e2f0;
|
||||
box-shadow: 0 10px 22px rgba(15, 23, 42, 0.08);
|
||||
padding: 14px 16px;
|
||||
z-index: 2;
|
||||
cursor: grab;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.node.dragging {
|
||||
cursor: grabbing;
|
||||
box-shadow: 0 18px 36px rgba(37, 99, 235, 0.18);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.node.large {
|
||||
width: 278px;
|
||||
min-height: 136px;
|
||||
}
|
||||
|
||||
.node.memory {
|
||||
width: 300px;
|
||||
min-height: 146px;
|
||||
border-style: dashed;
|
||||
border-width: 2px;
|
||||
box-shadow: 0 14px 30px rgba(37, 99, 235, 0.10);
|
||||
}
|
||||
|
||||
.node.human {
|
||||
width: 300px;
|
||||
min-height: 140px;
|
||||
border-width: 2px;
|
||||
box-shadow: 0 14px 28px rgba(217, 119, 6, 0.14);
|
||||
}
|
||||
|
||||
.node-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.node p {
|
||||
margin: 0 0 8px;
|
||||
color: var(--muted);
|
||||
font-size: 13.5px;
|
||||
line-height: 1.62;
|
||||
}
|
||||
|
||||
.chip-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.chip {
|
||||
white-space: nowrap;
|
||||
border-radius: 999px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid transparent;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.blue { border-color: #bfdbfe; background: var(--blue-soft); }
|
||||
.green { border-color: #bbf7d0; background: var(--green-soft); }
|
||||
.amber { border-color: #fde68a; background: var(--amber-soft); }
|
||||
.red { border-color: #fecaca; background: var(--red-soft); }
|
||||
.purple { border-color: #ddd6fe; background: var(--purple-soft); }
|
||||
.slate { border-color: #cbd5e1; background: var(--slate-soft); }
|
||||
|
||||
svg.connector-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.legend {
|
||||
margin-top: 18px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.25fr 1fr;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.legend-panel {
|
||||
padding: 18px 20px;
|
||||
}
|
||||
|
||||
.legend-panel h3 {
|
||||
margin: 0 0 14px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.legend-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
background: #f8fbff;
|
||||
border: 1px solid #e5ecf6;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.line-sample {
|
||||
width: 32px;
|
||||
height: 0;
|
||||
border-top: 3px solid;
|
||||
}
|
||||
|
||||
.line-main { border-color: var(--blue); }
|
||||
.line-feedback { border-color: var(--green); border-top-style: dashed; }
|
||||
.line-data { border-color: var(--purple); border-top-style: dotted; }
|
||||
|
||||
.notes {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
.page {
|
||||
padding: 14px;
|
||||
}
|
||||
.header,
|
||||
.legend {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.summary {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.summary {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.toolbar-actions {
|
||||
width: 100%;
|
||||
}
|
||||
.scale-indicator {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<div class="header">
|
||||
<section class="hero">
|
||||
<h1>电诈受害人资金核查多智能体协同流程图</h1>
|
||||
<p>
|
||||
本图聚焦“输入感知、证据解析、关联推理、人工复核、笔录与证据输出”全流程,
|
||||
突出三类核心关系:主处理链路、关键词回溯形成的自我完善闭环,以及民警人工复核的
|
||||
最终把关作用。页面已优化为可自适应缩放,适合浏览、截图和比赛汇报展示。
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section class="summary">
|
||||
<div class="metric">
|
||||
<div class="num">9</div>
|
||||
<div class="label">核心智能体/功能代理</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="num">1</div>
|
||||
<div class="label">案件级记忆中心</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="num">3</div>
|
||||
<div class="label">关键闭环:主链路/反馈链/人机链</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="num">22</div>
|
||||
<div class="label">主要数据流与关系连线</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="board">
|
||||
<div class="board-toolbar">
|
||||
<div class="toolbar-desc">
|
||||
页面问题已修复:支持自动适配窗口、手动缩放查看、图面主体优先展示。若用于截图,建议先切到
|
||||
<strong>100%</strong> 或 <strong>115%</strong>。
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<button type="button" data-scale-mode="fit" class="active">适配窗口</button>
|
||||
<button type="button" data-scale="0.9">90%</button>
|
||||
<button type="button" data-scale="1">100%</button>
|
||||
<button type="button" data-scale="1.15">115%</button>
|
||||
<button type="button" data-scale="1.3">130%</button>
|
||||
<span class="scale-indicator" id="scaleIndicator">缩放 100%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="board-shell" id="boardShell">
|
||||
<div class="board-viewport">
|
||||
<div class="board-inner">
|
||||
<div class="lane lane-input">
|
||||
<div class="lane-title">输入层</div>
|
||||
<div class="lane-subtitle">案件信息、截图材料、受害人陈述进入系统</div>
|
||||
</div>
|
||||
<div class="lane lane-sense">
|
||||
<div class="lane-title">感知层</div>
|
||||
<div class="lane-subtitle">页面识别、字段提取、页型修正</div>
|
||||
</div>
|
||||
<div class="lane lane-reason">
|
||||
<div class="lane-title">推理层</div>
|
||||
<div class="lane-subtitle">归并去重、中转识别、资金路径恢复、金额认定</div>
|
||||
</div>
|
||||
<div class="lane lane-review">
|
||||
<div class="lane-title">复核决策层</div>
|
||||
<div class="lane-subtitle">人工审核、补问提示、认定确认</div>
|
||||
</div>
|
||||
<div class="lane lane-output">
|
||||
<div class="lane-title">输出层</div>
|
||||
<div class="lane-subtitle">笔录内容、证据索引、图表与报告输出</div>
|
||||
</div>
|
||||
|
||||
<article id="node-input" class="node large" style="left: 70px; top: 110px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--slate);">入</span>
|
||||
多源案件输入
|
||||
</div>
|
||||
<p>受害人陈述、微信/支付宝/银行APP截图、交易凭证、补充说明。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip slate">案件基础信息</span>
|
||||
<span class="chip slate">多APP截图</span>
|
||||
<span class="chip slate">受害人描述</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-case" class="node" style="left: 385px; top: 108px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--blue);">1</span>
|
||||
案件受理智能体
|
||||
</div>
|
||||
<p>建立案件上下文,统一组织截图、人员、时间和案件编号等基础要素。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip blue">案件上下文</span>
|
||||
<span class="chip blue">任务编排起点</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-parse" class="node large" style="left: 690px; top: 240px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--blue);">2</span>
|
||||
截图解析智能体
|
||||
</div>
|
||||
<p>完成截图页型识别、字段抽取和候选交易生成,输出初始结构化结果。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip blue">页型判断</span>
|
||||
<span class="chip blue">字段抽取</span>
|
||||
<span class="chip blue">候选交易</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-feedback" class="node" style="left: 1030px; top: 245px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--green);">3</span>
|
||||
关键词回溯优化智能体
|
||||
</div>
|
||||
<p>利用后续高置信证据回溯“支付成功、账单详情、订单号、退款、充值”等关键词,反向修正页型判断。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip green">自我完善闭环</span>
|
||||
<span class="chip green">OCR校正</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-match" class="node large" style="left: 250px; top: 460px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--purple);">4</span>
|
||||
跨平台关联智能体
|
||||
</div>
|
||||
<p>基于订单号、金额、时间窗口、账户尾号、对手方相似性完成归并去重。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip purple">统一交易视图</span>
|
||||
<span class="chip purple">去重结果</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-flow" class="node large" style="left: 585px; top: 460px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--amber);">5</span>
|
||||
资金路径分析智能体
|
||||
</div>
|
||||
<p>识别本人账户中转、恢复流向关系、生成交易时间轴与资金流图基础数据。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip amber">中转识别</span>
|
||||
<span class="chip amber">路径恢复</span>
|
||||
<span class="chip amber">流图数据</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-assess" class="node large" style="left: 920px; top: 460px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--red);">6</span>
|
||||
金额认定智能体
|
||||
</div>
|
||||
<p>对交易进行高/中/低置信分层认定,生成认定理由、排除说明和待补问点位。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip red">高/中/低置信</span>
|
||||
<span class="chip red">认定理由</span>
|
||||
<span class="chip red">排除说明</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-inquiry" class="node" style="left: 1270px; top: 500px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--purple);">7</span>
|
||||
问询辅助智能体
|
||||
</div>
|
||||
<p>围绕中置信和待复核记录生成补问建议,辅助完善证据链和笔录提问重点。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip purple">追问建议</span>
|
||||
<span class="chip purple">缺口提示</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-review" class="node human" style="left: 690px; top: 715px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--amber);">审</span>
|
||||
民警人工复核
|
||||
</div>
|
||||
<p>对系统给出的认定结论进行确认、排除、补充核实,形成最终可办案结论。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip amber">确认</span>
|
||||
<span class="chip amber">排除</span>
|
||||
<span class="chip amber">补充核实</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-record" class="node large" style="left: 440px; top: 910px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--green);">8</span>
|
||||
笔录辅助智能体
|
||||
</div>
|
||||
<p>将已确认交易自动整理为可直接插入笔录的内容,包括时间、金额、渠道、对象和需补充说明点。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip green">笔录片段</span>
|
||||
<span class="chip green">内容直插</span>
|
||||
<span class="chip green">减轻负担</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-report" class="node large" style="left: 935px; top: 910px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--purple);">9</span>
|
||||
证据输出智能体
|
||||
</div>
|
||||
<p>输出交易明细、资金流图、时间轴、证据索引、Excel/PDF报告和审计快照。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip purple">证据清单</span>
|
||||
<span class="chip purple">报告导出</span>
|
||||
<span class="chip purple">快照留痕</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article id="node-memory" class="node memory" style="left: 90px; top: 698px;">
|
||||
<div class="node-title">
|
||||
<span class="icon" style="background: var(--blue);">库</span>
|
||||
案件记忆中心
|
||||
</div>
|
||||
<p>统一沉淀案件信息、截图、候选交易、去重结果、认定结果、复核状态、笔录片段和报告快照。</p>
|
||||
<div class="chip-row">
|
||||
<span class="chip blue">共享上下文</span>
|
||||
<span class="chip blue">持续更新</span>
|
||||
<span class="chip blue">结果回写</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<svg class="connector-layer" viewBox="0 0 1600 1060" preserveAspectRatio="none" aria-hidden="true">
|
||||
<defs>
|
||||
<marker id="arrowBlue" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#2563eb"></path>
|
||||
</marker>
|
||||
<marker id="arrowGreen" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#16a34a"></path>
|
||||
</marker>
|
||||
<marker id="arrowPurple" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#7c3aed"></path>
|
||||
</marker>
|
||||
<marker id="arrowAmber" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#d97706"></path>
|
||||
</marker>
|
||||
</defs>
|
||||
<g id="connectorPaths"></g>
|
||||
<g id="connectorLabels"></g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="legend">
|
||||
<div class="legend-panel">
|
||||
<h3>图例与阅读方式</h3>
|
||||
<div class="legend-grid">
|
||||
<div class="legend-item"><span class="line-sample line-main"></span>主处理流程</div>
|
||||
<div class="legend-item"><span class="line-sample line-feedback"></span>反馈优化闭环</div>
|
||||
<div class="legend-item"><span class="line-sample line-data"></span>共享数据/记忆回写</div>
|
||||
</div>
|
||||
<ul class="notes" style="margin-top: 16px;">
|
||||
<li>蓝色实线展示“截图输入到认定输出”的核心办案流程。</li>
|
||||
<li>绿色虚线闭环重点体现“证据识别后关键词回溯”的自我完善能力。</li>
|
||||
<li>橙色人工复核节点体现“机器先判、民警把关”的警务人机协同机制。</li>
|
||||
<li>输出层同时突出“笔录直接插入”和“证据化导出”两个实战落点。</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="legend-panel">
|
||||
<h3>本次修复点</h3>
|
||||
<ul class="notes">
|
||||
<li>修正统计卡片数量错误,核心智能体数量由 8 改为 9。</li>
|
||||
<li>新增“适配窗口/手动缩放”查看控制,解决普通笔记本屏幕下图面过小问题。</li>
|
||||
<li>优化主图容器,突出图面主体,减少页面留白对可读性的影响。</li>
|
||||
<li>提升节点文字、说明文字和连线标签对比度,更适合截图和投屏展示。</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const root = document.documentElement;
|
||||
const shell = document.getElementById("boardShell");
|
||||
const indicator = document.getElementById("scaleIndicator");
|
||||
const buttons = Array.from(document.querySelectorAll("[data-scale], [data-scale-mode]"));
|
||||
const baseWidth = 1600;
|
||||
let mode = "fit";
|
||||
let manualScale = 1;
|
||||
const boardInner = document.querySelector(".board-inner");
|
||||
const connectorPaths = document.getElementById("connectorPaths");
|
||||
const connectorLabels = document.getElementById("connectorLabels");
|
||||
const draggableNodes = Array.from(document.querySelectorAll(".node"));
|
||||
const connectors = [
|
||||
{ from: "node-input", to: "node-case", start: "right", end: "left", color: "#2563eb", marker: "url(#arrowBlue)", width: 3.5, label: "案件信息/截图材料", lx: 0, ly: -14 },
|
||||
{ from: "node-case", to: "node-parse", start: "right", end: "top", color: "#2563eb", marker: "url(#arrowBlue)", width: 3.5, label: "案件上下文", lx: 10, ly: -12 },
|
||||
{ from: "node-parse", to: "node-match", start: "bottom", end: "top", color: "#2563eb", marker: "url(#arrowBlue)", width: 3.5, label: "候选交易/页型结果", lx: -48, ly: -12, bend: 110 },
|
||||
{ from: "node-match", to: "node-flow", start: "right", end: "left", color: "#2563eb", marker: "url(#arrowBlue)", width: 3.5, label: "统一交易视图", lx: 0, ly: -12 },
|
||||
{ from: "node-flow", to: "node-assess", start: "right", end: "left", color: "#2563eb", marker: "url(#arrowBlue)", width: 3.5, label: "路径结果/中转标记", lx: 0, ly: -12 },
|
||||
{ from: "node-assess", to: "node-review", start: "bottom", end: "top", color: "#2563eb", marker: "url(#arrowBlue)", width: 3.5, label: "认定结果", lx: 0, ly: -10, bend: 96 },
|
||||
{ from: "node-assess", to: "node-inquiry", start: "right", end: "left", color: "#7c3aed", marker: "url(#arrowPurple)", width: 3, dash: "7 6", label: "待补问点位", lx: 6, ly: -12 },
|
||||
{ from: "node-inquiry", to: "node-review", start: "bottom", end: "right", color: "#7c3aed", marker: "url(#arrowPurple)", width: 3, dash: "7 6", label: "问询建议", lx: 38, ly: -12, bend: 120 },
|
||||
{ from: "node-assess", to: "node-feedback", start: "top", end: "bottom", color: "#16a34a", marker: "url(#arrowGreen)", width: 3.5, dash: "8 7", label: "高置信字段/后验信号", lx: 46, ly: -14, bend: 90 },
|
||||
{ from: "node-feedback", to: "node-parse", start: "left", end: "right", color: "#16a34a", marker: "url(#arrowGreen)", width: 3.5, dash: "8 7", label: "关键词回溯修正", lx: 10, ly: -12 },
|
||||
{ from: "node-review", to: "node-record", start: "bottom", end: "top", color: "#d97706", marker: "url(#arrowAmber)", width: 3.5, label: "已确认结论", lx: -22, ly: -12, bend: 78 },
|
||||
{ from: "node-review", to: "node-report", start: "bottom", end: "top", color: "#7c3aed", marker: "url(#arrowPurple)", width: 3.5, label: "证据索引/导出数据", lx: 28, ly: -12, bend: 78 },
|
||||
{ from: "node-case", to: "node-memory", start: "left", end: "top", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", label: "结果回写/共享上下文", lx: -86, ly: 18, bend: 120 },
|
||||
{ from: "node-parse", to: "node-memory", start: "left", end: "top", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", bend: 170 },
|
||||
{ from: "node-match", to: "node-memory", start: "left", end: "right", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", bend: 70 },
|
||||
{ from: "node-flow", to: "node-memory", start: "left", end: "right", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", bend: 130 },
|
||||
{ from: "node-review", to: "node-memory", start: "left", end: "right", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", bend: 86 },
|
||||
{ from: "node-record", to: "node-memory", start: "left", end: "bottom", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", bend: 120 },
|
||||
{ from: "node-report", to: "node-memory", start: "left", end: "bottom", color: "#7c3aed", marker: "url(#arrowPurple)", width: 2.6, dash: "3 7", bend: 220 }
|
||||
];
|
||||
|
||||
function setActiveButton(target) {
|
||||
buttons.forEach((btn) => btn.classList.remove("active"));
|
||||
if (target) target.classList.add("active");
|
||||
}
|
||||
|
||||
function computeFitScale() {
|
||||
const available = shell.clientWidth - 24;
|
||||
const fit = available / baseWidth;
|
||||
return Math.max(0.68, Math.min(1, fit));
|
||||
}
|
||||
|
||||
function applyScale(value) {
|
||||
root.style.setProperty("--diagram-scale", String(value));
|
||||
indicator.textContent = `缩放 ${Math.round(value * 100)}%`;
|
||||
}
|
||||
|
||||
function updateScale() {
|
||||
applyScale(mode === "fit" ? computeFitScale() : manualScale);
|
||||
}
|
||||
|
||||
function getNodeRect(id) {
|
||||
const node = document.getElementById(id);
|
||||
return {
|
||||
el: node,
|
||||
left: parseFloat(node.style.left || "0"),
|
||||
top: parseFloat(node.style.top || "0"),
|
||||
width: node.offsetWidth,
|
||||
height: node.offsetHeight
|
||||
};
|
||||
}
|
||||
|
||||
function getAnchorPoint(rect, anchor) {
|
||||
switch (anchor) {
|
||||
case "left":
|
||||
return { x: rect.left, y: rect.top + rect.height / 2, dx: -1, dy: 0 };
|
||||
case "right":
|
||||
return { x: rect.left + rect.width, y: rect.top + rect.height / 2, dx: 1, dy: 0 };
|
||||
case "top":
|
||||
return { x: rect.left + rect.width / 2, y: rect.top, dx: 0, dy: -1 };
|
||||
case "bottom":
|
||||
default:
|
||||
return { x: rect.left + rect.width / 2, y: rect.top + rect.height, dx: 0, dy: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
function cubicPoint(p0, p1, p2, p3, t) {
|
||||
const mt = 1 - t;
|
||||
return {
|
||||
x: mt ** 3 * p0.x + 3 * mt ** 2 * t * p1.x + 3 * mt * t ** 2 * p2.x + t ** 3 * p3.x,
|
||||
y: mt ** 3 * p0.y + 3 * mt ** 2 * t * p1.y + 3 * mt * t ** 2 * p2.y + t ** 3 * p3.y
|
||||
};
|
||||
}
|
||||
|
||||
function renderConnectors() {
|
||||
connectorPaths.innerHTML = "";
|
||||
connectorLabels.innerHTML = "";
|
||||
|
||||
connectors.forEach((conn) => {
|
||||
const fromRect = getNodeRect(conn.from);
|
||||
const toRect = getNodeRect(conn.to);
|
||||
const start = getAnchorPoint(fromRect, conn.start);
|
||||
const end = getAnchorPoint(toRect, conn.end);
|
||||
const bend = conn.bend || 84;
|
||||
|
||||
const cp1 = {
|
||||
x: start.x + start.dx * bend,
|
||||
y: start.y + start.dy * bend
|
||||
};
|
||||
const cp2 = {
|
||||
x: end.x + end.dx * bend,
|
||||
y: end.y + end.dy * bend
|
||||
};
|
||||
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("d", `M ${start.x} ${start.y} C ${cp1.x} ${cp1.y}, ${cp2.x} ${cp2.y}, ${end.x} ${end.y}`);
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke", conn.color);
|
||||
path.setAttribute("stroke-width", String(conn.width));
|
||||
if (conn.dash) path.setAttribute("stroke-dasharray", conn.dash);
|
||||
path.setAttribute("marker-end", conn.marker);
|
||||
connectorPaths.appendChild(path);
|
||||
|
||||
if (conn.label) {
|
||||
const mid = cubicPoint(start, cp1, cp2, end, 0.5);
|
||||
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
||||
label.setAttribute("x", String(mid.x + (conn.lx || 0)));
|
||||
label.setAttribute("y", String(mid.y + (conn.ly || 0)));
|
||||
label.setAttribute("font-size", "12.5");
|
||||
label.setAttribute("fill", conn.color);
|
||||
label.setAttribute("font-weight", "800");
|
||||
label.textContent = conn.label;
|
||||
connectorLabels.appendChild(label);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
if (button.dataset.scaleMode === "fit") {
|
||||
mode = "fit";
|
||||
setActiveButton(button);
|
||||
updateScale();
|
||||
return;
|
||||
}
|
||||
mode = "manual";
|
||||
manualScale = Number(button.dataset.scale || "1");
|
||||
setActiveButton(button);
|
||||
updateScale();
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
if (mode === "fit") updateScale();
|
||||
renderConnectors();
|
||||
});
|
||||
|
||||
draggableNodes.forEach((node) => {
|
||||
let dragging = false;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let originLeft = 0;
|
||||
let originTop = 0;
|
||||
|
||||
node.addEventListener("mousedown", (event) => {
|
||||
if (event.button !== 0) return;
|
||||
dragging = true;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
originLeft = parseFloat(node.style.left || "0");
|
||||
originTop = parseFloat(node.style.top || "0");
|
||||
node.classList.add("dragging");
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
window.addEventListener("mousemove", (event) => {
|
||||
if (!dragging) return;
|
||||
|
||||
const currentScale = parseFloat(
|
||||
getComputedStyle(document.documentElement).getPropertyValue("--diagram-scale")
|
||||
) || 1;
|
||||
|
||||
const deltaX = (event.clientX - startX) / currentScale;
|
||||
const deltaY = (event.clientY - startY) / currentScale;
|
||||
|
||||
const maxLeft = boardInner.clientWidth - node.offsetWidth - 12;
|
||||
const maxTop = boardInner.clientHeight - node.offsetHeight - 12;
|
||||
|
||||
const nextLeft = Math.min(Math.max(12, originLeft + deltaX), maxLeft);
|
||||
const nextTop = Math.min(Math.max(12, originTop + deltaY), maxTop);
|
||||
|
||||
node.style.left = `${nextLeft}px`;
|
||||
node.style.top = `${nextTop}px`;
|
||||
renderConnectors();
|
||||
});
|
||||
|
||||
window.addEventListener("mouseup", () => {
|
||||
if (!dragging) return;
|
||||
dragging = false;
|
||||
node.classList.remove("dragging");
|
||||
});
|
||||
});
|
||||
|
||||
updateScale();
|
||||
renderConnectors();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user