feat(原型设计): 增强强Agent对话工作台原型

This commit is contained in:
2026-06-03 22:31:23 +08:00
parent 5a137b5b45
commit 54fc1baa4c

View File

@@ -6,24 +6,27 @@
<title>注册审核平台原型演示站</title>
<style>
:root {
--bg: #edf2f6;
--bg-strong: #dfe8ef;
--panel: rgba(255, 255, 255, 0.92);
--bg: #eef2f5;
--bg-strong: #d9e1e8;
--panel: #ffffff;
--panel-strong: #ffffff;
--ink: #122233;
--muted: #5f7285;
--line: #d5dee6;
--navy: #153d62;
--teal: #1f6b7a;
--accent: #d6803b;
--danger: #b54a34;
--success: #2f7d57;
--warning: #cb8a2f;
--shadow: 0 18px 40px rgba(28, 48, 73, 0.12);
--radius-xl: 28px;
--radius-lg: 20px;
--radius-md: 14px;
--radius-sm: 10px;
--ink: #132535;
--muted: #627588;
--line: #d7e0e7;
--line-strong: #c4d0da;
--navy: #173a58;
--navy-strong: #112b42;
--teal: #245d69;
--accent: #b77a39;
--danger: #a94731;
--success: #2f6a4b;
--warning: #b8802f;
--shadow: 0 8px 18px rgba(23, 41, 61, 0.06);
--shadow-strong: 0 12px 24px rgba(18, 35, 53, 0.08);
--radius-xl: 10px;
--radius-lg: 8px;
--radius-md: 6px;
--radius-sm: 4px;
}
* {
@@ -35,9 +38,7 @@
font-family: "Microsoft YaHei", "PingFang SC", "Segoe UI", sans-serif;
color: var(--ink);
background:
radial-gradient(circle at top left, rgba(59, 108, 143, 0.18), transparent 30%),
radial-gradient(circle at bottom right, rgba(215, 128, 59, 0.14), transparent 30%),
linear-gradient(180deg, #f6f9fb 0%, #edf2f6 100%);
linear-gradient(180deg, #f5f7f9 0%, #edf2f5 100%);
}
a {
@@ -61,19 +62,20 @@
position: sticky;
top: 0;
height: 100vh;
padding: 24px 18px;
background: linear-gradient(180deg, #163754 0%, #10283f 100%);
padding: 18px 14px;
background: linear-gradient(180deg, #173754 0%, #122c43 100%);
color: rgba(255, 255, 255, 0.94);
display: flex;
flex-direction: column;
gap: 18px;
gap: 12px;
border-right: 1px solid rgba(255, 255, 255, 0.08);
}
.brand {
padding: 18px;
padding: 16px;
border-radius: var(--radius-lg);
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.brand h1 {
@@ -105,9 +107,9 @@
.nav-btn {
width: 100%;
padding: 14px 16px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.06);
padding: 12px 14px;
border-radius: var(--radius-md);
background: rgba(255, 255, 255, 0.03);
color: white;
text-align: left;
border: 1px solid transparent;
@@ -122,20 +124,21 @@
.nav-btn.active,
.nav-btn:hover {
background: rgba(255, 255, 255, 0.14);
background: rgba(255, 255, 255, 0.09);
border-color: rgba(255, 255, 255, 0.12);
transform: translateX(2px);
transform: none;
}
.sidebar-summary {
margin-top: auto;
padding: 16px;
border-radius: var(--radius-lg);
background: rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.main {
padding: 22px 24px 30px;
padding: 16px 18px 24px;
}
.topbar {
@@ -143,18 +146,18 @@
justify-content: space-between;
gap: 16px;
align-items: center;
padding: 18px 22px;
padding: 16px 18px;
border-radius: var(--radius-xl);
background: rgba(255, 255, 255, 0.84);
border: 1px solid rgba(255, 255, 255, 0.8);
background: #ffffff;
border: 1px solid var(--line);
box-shadow: var(--shadow);
backdrop-filter: blur(18px);
margin-bottom: 22px;
margin-bottom: 16px;
}
.topbar h2 {
margin: 0 0 6px;
font-size: 26px;
font-size: 22px;
letter-spacing: 0.01em;
}
.status-row,
@@ -174,19 +177,20 @@
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 12px;
padding: 6px 10px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
}
.status-chip {
background: #edf5ff;
color: var(--navy);
background: #eef3f8;
color: #264764;
border: 1px solid #d6e0ea;
}
.tag {
background: #f5f7fa;
background: #f6f8fa;
color: var(--muted);
border: 1px solid var(--line);
}
@@ -209,27 +213,28 @@
.primary-btn,
.secondary-btn {
padding: 11px 16px;
border-radius: 12px;
padding: 10px 14px;
border-radius: var(--radius-md);
font-weight: 700;
}
.primary-btn {
background: linear-gradient(135deg, #1d5b8f 0%, #123c62 100%);
background: linear-gradient(180deg, #214b74 0%, #163754 100%);
color: white;
box-shadow: 0 10px 24px rgba(21, 61, 98, 0.24);
box-shadow: none;
border: 1px solid #153754;
}
.secondary-btn,
.mini-btn {
background: white;
color: var(--ink);
border: 1px solid var(--line);
border: 1px solid var(--line-strong);
}
.mini-btn {
padding: 8px 10px;
border-radius: 10px;
padding: 7px 10px;
border-radius: var(--radius-md);
font-size: 12px;
font-weight: 700;
}
@@ -247,16 +252,15 @@
display: grid;
grid-template-columns: 1.35fr 1fr;
gap: 18px;
margin-bottom: 18px;
margin-bottom: 16px;
}
.panel {
background: var(--panel);
border: 1px solid rgba(255, 255, 255, 0.8);
border: 1px solid var(--line);
border-radius: var(--radius-xl);
box-shadow: var(--shadow);
padding: 20px;
backdrop-filter: blur(18px);
padding: 18px;
}
.panel h3,
@@ -273,24 +277,24 @@
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
margin-bottom: 18px;
margin-bottom: 16px;
}
.metric-card,
.task-card,
.info-card {
border-radius: var(--radius-lg);
padding: 18px;
padding: 16px;
background: var(--panel-strong);
border: 1px solid var(--line);
}
.metric-card strong {
display: block;
font-size: 30px;
margin-top: 10px;
font-size: 28px;
margin-top: 8px;
line-height: 1;
color: var(--navy);
color: var(--navy-strong);
}
.section-title {
@@ -315,7 +319,7 @@
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
margin-bottom: 18px;
margin-bottom: 12px;
}
.task-card {
@@ -325,21 +329,22 @@
}
.task-card.active {
border-color: rgba(21, 61, 98, 0.28);
box-shadow: 0 18px 34px rgba(21, 61, 98, 0.12);
border-color: #8da4b8;
box-shadow: none;
background: #fbfdff;
}
.task-card.active::after {
content: "";
position: absolute;
inset: 0;
border-top: 5px solid var(--navy);
border-top: 3px solid var(--navy);
border-radius: inherit;
}
.task-card h4 {
margin: 0 0 8px;
font-size: 17px;
font-size: 16px;
}
.task-card p {
@@ -347,7 +352,7 @@
color: var(--muted);
min-height: 36px;
line-height: 1.5;
font-size: 13px;
font-size: 12px;
}
.task-meta {
@@ -397,10 +402,10 @@
.issue-item,
.check-item,
.governance-row {
padding: 14px;
border-radius: 14px;
background: #f8fafc;
border: 1px solid #e1e8ee;
padding: 12px;
border-radius: var(--radius-md);
background: #f8fafb;
border: 1px solid #dbe4eb;
}
.tree-node button {
@@ -471,9 +476,9 @@
.evidence {
padding: 14px;
border-radius: 16px;
background: linear-gradient(180deg, #f6f9fd 0%, #edf3f8 100%);
border: 1px solid #dbe5ef;
border-radius: var(--radius-md);
background: #f5f8fb;
border: 1px solid #d6e1ea;
line-height: 1.7;
color: #2f4358;
}
@@ -485,7 +490,7 @@
.alert {
padding: 14px 16px;
border-radius: 16px;
border-radius: var(--radius-md);
border: 1px solid;
font-size: 14px;
line-height: 1.65;
@@ -515,10 +520,9 @@
right: -720px;
width: min(720px, 100vw);
height: 100vh;
background: rgba(248, 251, 253, 0.97);
backdrop-filter: blur(18px);
box-shadow: -24px 0 40px rgba(14, 29, 43, 0.2);
border-left: 1px solid rgba(255, 255, 255, 0.7);
background: #f7fafc;
box-shadow: -16px 0 24px rgba(14, 29, 43, 0.12);
border-left: 1px solid var(--line);
transition: right 0.28s ease;
z-index: 20;
display: flex;
@@ -557,7 +561,7 @@
padding: 10px 12px;
border-radius: 999px;
background: white;
border: 1px solid var(--line);
border: 1px solid var(--line-strong);
color: var(--muted);
font-weight: 700;
}
@@ -584,11 +588,11 @@
}
.message-card {
border-radius: 22px;
background: linear-gradient(180deg, #ffffff 0%, #f7fafc 100%);
border: 1px solid #dce5ec;
padding: 20px;
box-shadow: 0 16px 28px rgba(24, 40, 54, 0.1);
border-radius: var(--radius-lg);
background: #ffffff;
border: 1px solid #d6e0e7;
padding: 18px;
box-shadow: none;
}
.message-card .card-head {
@@ -615,9 +619,9 @@
align-items: center;
gap: 12px;
padding: 16px;
border-radius: 16px;
background: #f6fafc;
border: 1px dashed #bad0df;
border-radius: var(--radius-md);
background: #f7fafc;
border: 1px solid #cfdae3;
}
.copy-grid {
@@ -626,6 +630,170 @@
gap: 14px;
}
.workspace-main {
display: grid;
grid-template-columns: 1.5fr 0.95fr;
gap: 16px;
margin-top: 16px;
}
.agent-shell {
display: grid;
gap: 12px;
}
.prompt-bar {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.prompt-chip {
padding: 8px 12px;
border-radius: var(--radius-md);
background: #f3f6f9;
border: 1px solid var(--line);
color: var(--navy-strong);
font-size: 12px;
font-weight: 700;
}
.conversation-board {
display: grid;
gap: 12px;
}
.message {
border: 1px solid var(--line);
border-radius: var(--radius-lg);
background: #ffffff;
overflow: hidden;
}
.message.user {
border-color: #cdd9e2;
background: #f7fafc;
}
.message.agent {
border-color: #cfdae3;
background: #ffffff;
}
.message.tool {
border-color: #d7dfe6;
background: #fbfcfd;
}
.message.system {
border-color: #d9e1e8;
background: #f6f8fa;
}
.message-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-bottom: 1px solid #e6edf2;
background: #f7fafc;
}
.message.user .message-head {
background: #eef4f8;
}
.message.agent .message-head {
background: #f5f8fb;
}
.message-body {
padding: 14px;
line-height: 1.75;
color: #314659;
}
.message-body p {
margin: 0 0 10px;
}
.message-body p:last-child {
margin-bottom: 0;
}
.trace-grid {
display: grid;
gap: 10px;
margin-top: 12px;
}
.trace-item {
padding: 12px;
border-radius: var(--radius-md);
border: 1px solid #dbe4eb;
background: #f8fafb;
}
.trace-item strong {
display: block;
margin-bottom: 6px;
}
.review-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.review-btn {
padding: 8px 12px;
border-radius: var(--radius-md);
border: 1px solid var(--line-strong);
background: #ffffff;
color: var(--ink);
font-size: 12px;
font-weight: 700;
}
.review-btn.primary {
background: #173754;
border-color: #173754;
color: #ffffff;
}
.page-agent-grid {
display: grid;
grid-template-columns: 1.35fr 0.95fr;
gap: 16px;
margin-top: 16px;
}
.page-agent-side {
display: grid;
gap: 16px;
}
.agent-mini-panel {
display: grid;
gap: 12px;
}
.agent-q-list {
display: grid;
gap: 8px;
}
.agent-q-item {
padding: 10px 12px;
border: 1px solid var(--line);
border-radius: var(--radius-md);
background: #f8fafb;
font-size: 13px;
color: #314659;
}
.list-inline {
display: flex;
flex-wrap: wrap;
@@ -672,7 +840,9 @@
.two-col,
.tri-col,
.split,
.subsplit {
.subsplit,
.workspace-main,
.page-agent-grid {
grid-template-columns: 1fr;
}
@@ -881,6 +1051,121 @@
receipt: { id: "om_demo_message_20260603_103000", time: "2026-06-03 10:30:00" },
webLink: "http://localhost:8000/audit/1001/",
},
agent: {
quickPrompts: [
"检查当前资料完整性",
"抽取本批次核心字段",
"解释产品名称冲突原因",
"生成整改建议和责任人分发"
],
workspaceConversation: [
{
role: "user",
title: "用户指令",
meta: "10:05 · 审核任务工作台",
body: [
"请基于当前批次资料,按注册申报流程完成完整性检查、字段抽取、一致性核查,并给出风险结论。"
]
},
{
role: "agent",
title: "Agent 计划",
meta: "10:05 · orchestration",
body: [
"我将按 5 个阶段执行:读取资料包目录 -> 加载法规规则包 -> 抽取统一字段池 -> 执行强一致比对 -> 汇总风险并判断是否允许导出。",
"当前建议优先关注 CH1 监管信息和说明书相关字段。"
]
},
{
role: "tool",
title: "工具调用记录",
meta: "10:06 · tools",
body: [
"已调用 `scan_submission_package`、`check_nmpa_completeness`、`extract_product_fields`、`compare_field_consistency`、`build_risk_alerts`。",
"RAG 命中来源包括《体外诊断试剂注册申报资料要求及说明》与目标产品说明书切片。"
]
},
{
role: "system",
title: "结构化结论",
meta: "10:09 · result",
body: [
"完整性检查:发现 1 个缺失项、1 个错放项。",
"字段抽取13 个目标字段中 12 个已抽取1 个待复核。",
"一致性核查:产品名称存在冲突,疑似混档。",
"综合风险:高风险,不允许正式导出,仅允许生成草稿。"
]
}
],
workspaceTrace: [
{ label: "当前任务", value: "注册申报审核闭环执行" },
{ label: "命中规则", value: "nmpa_ivd_registration_v1 / ivd_strict_consistency_v1" },
{ label: "工具链路", value: "资料扫描 -> 完整性检查 -> 字段抽取 -> 一致性 -> 风险汇总" },
{ label: "人工复核点", value: "储存条件字段 / 产品名称冲突 / CH1 缺失项确认" }
],
pagePanels: {
completeness: {
question: "为什么 CH1.11.4 会被判定为缺失?",
answer: "Agent 先读取目录汇总结果,再将 CH1 章节与法规规则包逐项匹配。规则要求 CH1.11.4 属于必交声明类资料但当前资料包未识别到有效命中文档因此判定为缺失。RAG 证据仅用于引用法规原文,不改变规则结论。",
trace: [
"规则命中CH1.11.4 -> 必交项 -> 默认高风险",
"证据来源:法规要求说明 / CH1 监管信息章节",
"建议动作:补充资料后重跑完整性检查"
],
actions: ["确认缺失项", "标记疑似命中", "发起补件通知"]
},
fields: {
question: "储存条件为什么是待复核,而不是直接回填?",
answer: "该字段来自申请表表格抽取原文字段表述较模糊和说明书标准口径未形成稳定一对一映射。Agent 将其保留在字段池中,但置信度标记为低,并要求人工确认后才能进入正式导出。",
trace: [
"抽取方式:表格抽取 + 标准化",
"字段状态manual_review_required = true",
"影响范围:正式导出时触发阻断"
],
actions: ["采用推荐值", "驳回推荐值", "重新抽取"]
},
consistency: {
question: "产品名称冲突为什么会被上升为混档风险?",
answer: "因为当前审核范围内的说明书与申请表属于同一批次业务资料,而强一致规则要求产品名称必须完全一致。两份文档标准化后仍不一致,因此 Agent 将其识别为字段冲突,并进一步提示疑似跨产品资料混入。",
trace: [
"强一致字段product_name",
"来源文档:目标产品说明书 / CH1.4 申请表",
"处理建议:先确认审核范围,再继续导出"
],
actions: ["确认混档风险", "排除文档后重跑", "采用说明书版本"]
},
risk: {
question: "为什么最终是不通过,而不是待复核?",
answer: "风险规则中配置了 high_risk_policy = fail。当前批次同时存在缺失必交资料和产品名称冲突两个高风险项因此 Agent 直接给出不通过结论,而不是仅提示待复核。",
trace: [
"风险规则ivd_registration_risk_v1",
"高风险项:缺失必交资料 / 产品名称冲突",
"下游影响:正式导出阻断 / 飞书通知责任人"
],
actions: ["确认风险结论", "生成整改任务", "推送飞书通知"]
},
word: {
question: "为什么只能生成草稿版,不能正式导出?",
answer: "Word 导出步骤会读取一致性核查和风险预警结果。当前批次存在高风险和待复核字段因此回填拦截检查命中正式导出阻断条件Agent 只允许生成带审阅用途的草稿版。",
trace: [
"模板registration_application_form_v1",
"阻断原因:高风险 + 冲突字段 + 待复核字段",
"导出策略allow_draft_when_blocked = true"
],
actions: ["生成草稿", "查看阻断详情", "整改后重试"]
},
notification: {
question: "飞书通知里为什么要同时 @ 两个责任人?",
answer: "因为责任人映射同时命中了章节缺失和风险类型冲突两类规则。CH1 缺失项对应注册申报负责人,产品名称冲突对应注册资料负责人,所以 Agent 在同一张消息卡片中同时 @ 两位责任人,并附上 Web 详情入口。",
trace: [
"责任人映射CH1 -> 注册资料负责人",
"风险映射missing_required_document -> 注册申报负责人",
"通知载荷interactive_card + Web detail URL"
],
actions: ["发送通知", "切换消息模板", "查看回执日志"]
}
}
},
governance: [
{
id: "rules",
@@ -1005,6 +1290,89 @@
`).join("")}</div>`;
}
function renderAgentConversation(messages) {
return `
<div class="conversation-board">
${messages.map(message => `
<div class="message ${message.role}">
<div class="message-head">
<strong>${message.title}</strong>
<span class="tag">${message.meta}</span>
</div>
<div class="message-body">
${message.body.map(line => `<p>${line}</p>`).join("")}
</div>
</div>
`).join("")}
</div>
`;
}
function renderTraceItems(items) {
return `
<div class="trace-grid">
${items.map(item => `
<div class="trace-item">
<strong>${item.label || item}</strong>
${item.value ? `<div class="muted" style="color:#5f7285;">${item.value}</div>` : ""}
</div>
`).join("")}
</div>
`;
}
function renderAgentSidePanel(key, title) {
const panel = data.agent.pagePanels[key];
if (!panel) return "";
return `
<div class="page-agent-side">
<div class="panel">
<div class="section-title">
<div>
<h3>Agent 页面追问</h3>
<p>${title}中的解释型对话与证据追溯。</p>
</div>
</div>
<div class="agent-mini-panel">
<div class="message user">
<div class="message-head">
<strong>追问</strong>
<span class="tag">用户</span>
</div>
<div class="message-body">
<p>${panel.question}</p>
</div>
</div>
<div class="message agent">
<div class="message-head">
<strong>Agent 解释</strong>
<span class="tag">当前页面上下文</span>
</div>
<div class="message-body">
<p>${panel.answer}</p>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="section-title">
<div>
<h3>执行轨迹</h3>
<p>说明这个结论是怎么来的。</p>
</div>
</div>
<div class="agent-q-list">
${panel.trace.map(item => `<div class="agent-q-item">${item}</div>`).join("")}
</div>
<div class="review-actions">
${panel.actions.map((action, index) => `<button class="review-btn ${index === 0 ? "primary" : ""}">${action}</button>`).join("")}
</div>
</div>
</div>
`;
}
function renderWorkspacePage() {
return `
<section class="page active" data-page="workspace">
@@ -1106,6 +1474,49 @@
</div>
</div>
</div>
<div class="workspace-main">
<div class="agent-shell">
<div class="panel">
<div class="section-title">
<div>
<h3>Agent 主对话区</h3>
<p>体现用户指令、Agent 计划、工具调用和结构化结论的多轮协作过程。</p>
</div>
</div>
<div class="prompt-bar">
${data.agent.quickPrompts.map(prompt => `<span class="prompt-chip">${prompt}</span>`).join("")}
</div>
<div style="margin-top:12px;">
${renderAgentConversation(data.agent.workspaceConversation)}
</div>
</div>
</div>
<div class="page-agent-side">
<div class="panel">
<div class="section-title">
<div>
<h3>Agent 执行轨迹</h3>
<p>把这次审核从“聊天”升级为“可追踪执行”。</p>
</div>
</div>
${renderTraceItems(data.agent.workspaceTrace)}
</div>
<div class="panel">
<div class="section-title">
<div>
<h3>人工复核协同</h3>
<p>支持人机共审,而不是单向回答。</p>
</div>
</div>
<div class="review-actions">
<button class="review-btn primary">确认当前结论</button>
<button class="review-btn">采用说明书值</button>
<button class="review-btn">重新执行一致性核查</button>
<button class="review-btn">生成整改任务</button>
</div>
</div>
</div>
</div>
</section>
`;
}
@@ -1243,6 +1654,8 @@
return `
<section class="page" data-page="completeness">
${metricCards(data.completeness.summary.map(item => ({ label: item.label, value: item.value, note: "" })))}
<div class="page-agent-grid">
<div>
<div class="split">
<div class="panel">
<div class="section-title">
@@ -1334,6 +1747,9 @@
</table>
</div>
</div>
</div>
${renderAgentSidePanel("completeness", "法规完整性检查")}
</div>
</section>
`;
}
@@ -1342,6 +1758,8 @@
return `
<section class="page" data-page="fields">
${metricCards(data.fields.summary.map(item => ({ label: item.label, value: item.value, note: "" })))}
<div class="page-agent-grid">
<div>
<div class="panel">
<div class="section-title">
<div>
@@ -1409,6 +1827,9 @@
</div>
</div>
</div>
</div>
${renderAgentSidePanel("fields", "字段抽取与字段池")}
</div>
</section>
`;
}
@@ -1417,6 +1838,8 @@
return `
<section class="page" data-page="consistency">
${metricCards(data.consistency.summary.map(item => ({ label: item.label, value: item.value, note: "" })))}
<div class="page-agent-grid">
<div>
<div class="two-col">
<div class="panel">
<div class="section-title">
@@ -1479,6 +1902,9 @@
</div>
<div class="alert danger">说明书与申请表中的产品名称存在文本不一致,系统判定为疑似跨产品资料混入,建议先确认当前审核范围再继续回填和导出。</div>
</div>
</div>
${renderAgentSidePanel("consistency", "一致性核查")}
</div>
</section>
`;
}
@@ -1487,6 +1913,8 @@
return `
<section class="page" data-page="risk">
${metricCards(data.risk.summary.map(item => ({ label: item.label, value: item.value, note: "" })))}
<div class="page-agent-grid">
<div>
<div class="hero">
<div class="panel">
<div class="section-title">
@@ -1549,6 +1977,9 @@
</tbody>
</table>
</div>
</div>
${renderAgentSidePanel("risk", "风险预警")}
</div>
</section>
`;
}
@@ -1562,6 +1993,8 @@
{ label: "阻断项", value: "3", note: "高风险 / 冲突 / 待复核" },
{ label: "导出状态", value: "草稿已生成", note: "正式版被阻断" },
])}
<div class="page-agent-grid">
<div>
<div class="split">
<div class="panel">
<div class="section-title">
@@ -1632,6 +2065,9 @@
<button class="primary-btn">下载草稿</button>
</div>
</div>
</div>
${renderAgentSidePanel("word", "Word 回填导出")}
</div>
</section>
`;
}
@@ -1639,6 +2075,8 @@
function renderNotificationPage() {
return `
<section class="page" data-page="notification">
<div class="page-agent-grid">
<div>
<div class="split">
<div class="panel">
<div class="section-title">
@@ -1695,6 +2133,9 @@
</div>
</div>
</div>
</div>
${renderAgentSidePanel("notification", "飞书通知视图")}
</div>
</section>
`;
}