feat(原型设计): 关联资料包与对话记录

This commit is contained in:
2026-06-03 23:48:53 +08:00
parent aa5d4d77f8
commit 7836690303
2 changed files with 113 additions and 345 deletions

View File

@@ -643,6 +643,10 @@
color: var(--ink);
}
.search-input::placeholder {
color: #9aa8bf;
}
.system-nav {
display: flex;
align-items: stretch;
@@ -837,7 +841,9 @@
conversations: [
{
id: "conv-001",
title: "注册批次审核主线",
packageId: "pkg-001",
productName: "新型冠状病毒 2019-nCoV 核酸检测试剂盒",
title: "新型冠状病毒 2019-nCoV 核酸检测试剂盒",
time: "今天 10:05",
status: "高风险",
summary: "已完成目录汇总、完整性检查、字段抽取与一致性核查。",
@@ -948,7 +954,9 @@
},
{
id: "conv-002",
title: "仅做目录汇总",
packageId: "pkg-002",
productName: "乙型肝炎病毒核酸检测试剂盒",
title: "乙型肝炎病毒核酸检测试剂盒",
time: "昨天 16:40",
status: "已完成",
summary: "生成目录、页数和章节点结果,未继续完整性核查。",
@@ -981,6 +989,7 @@
],
activeConversationId: "conv-001",
activeAbilityKey: "import",
materialSearchQuery: "",
knowledge: {
metrics: [
{ label: "知识库资料总数", value: "27", note: "法规、模板、示例资料、内部 SOP" },
@@ -1076,6 +1085,28 @@
{ label: "章节点命中", value: "14", note: "覆盖 CH1 至 CH6 关键章节" },
{ label: "目录异常", value: "2", note: "1 份页数待复核1 份疑似目录错放" }
],
packages: [
{
id: "pkg-001",
conversationId: "conv-001",
productName: "新型冠状病毒 2019-nCoV 核酸检测试剂盒",
batchId: "SUB-20260603-001",
fileCount: "18",
pages: "236",
status: "审核中",
note: "已完成目录汇总并进入完整性检查。"
},
{
id: "pkg-002",
conversationId: "conv-002",
productName: "乙型肝炎病毒核酸检测试剂盒",
batchId: "SUB-20260602-004",
fileCount: "12",
pages: "148",
status: "已完成",
note: "仅执行目录汇总与页数识别。"
}
],
items: [
{ path: "第1章 监管信息/CH1.2 监管信息目录.docx", chapter: "CH1.2", pages: "3", status: "已识别", note: "命中监管信息目录" },
{ path: "第1章 监管信息/CH1.4 申请表.docx", chapter: "CH1.4", pages: "5", status: "已识别", note: "用于字段抽取与一致性核查" },
@@ -1094,6 +1125,18 @@
return data.conversations.find(item => item.id === data.activeConversationId) || data.conversations[0];
}
function getFilteredMaterialPackages() {
const query = data.materialSearchQuery.trim().toLowerCase();
if (!query) {
return data.materials.packages;
}
return data.materials.packages.filter(item =>
item.productName.toLowerCase().includes(query)
|| item.batchId.toLowerCase().includes(query)
);
}
function statusClass(text) {
if (text.includes("高风险") || text.includes("阻断") || text.includes("失败")) return "danger";
if (text.includes("待复核") || text.includes("处理中") || text.includes("解析中")) return "warning";
@@ -1307,6 +1350,9 @@
}
function renderMaterialsPage() {
const filteredPackages = getFilteredMaterialPackages();
const activeConversation = getActiveConversation();
return `
<section class="page ${activeTabId === "materials" ? "active" : ""}" data-page="materials">
${renderMetrics(data.materials.metrics)}
@@ -1314,7 +1360,7 @@
<div class="panel-head">
<div>
<h2>资料包总览</h2>
<p>直接承接原始材料目录,用于上传后快速看到文件结构、章节点和异常。</p>
<p>资料包与对话记录一一关联,对话名称直接采用解析后的产品名称。</p>
</div>
<div class="table-actions">
<button class="mini-btn">导入资料包</button>
@@ -1323,6 +1369,49 @@
</div>
</div>
<div class="panel-body">
<div class="search-bar">
<input
class="search-input"
id="materialSearchInput"
value="${data.materialSearchQuery}"
placeholder="根据产品名称或批次号搜索资料包"
/>
<button class="secondary-btn">搜索资料包</button>
<button class="secondary-btn">清空搜索</button>
</div>
<div class="table-shell" style="margin-bottom:16px;">
<table>
<thead>
<tr>
<th>产品名称</th>
<th>关联对话</th>
<th>批次号</th>
<th>文件数</th>
<th>页数</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
${filteredPackages.map(item => `
<tr>
<td>${item.productName}</td>
<td>${item.productName}</td>
<td>${item.batchId}</td>
<td>${item.fileCount}</td>
<td>${item.pages}</td>
<td><span class="status-tag ${statusClass(item.status)}">${item.status}</span></td>
<td><button class="mini-btn" data-open-conversation="${item.conversationId}">查看对话</button></td>
</tr>
`).join("")}
${filteredPackages.length === 0 ? `
<tr>
<td colspan="7">未找到匹配的产品名称资料包。</td>
</tr>
` : ""}
</tbody>
</table>
</div>
<div class="table-shell">
<table>
<thead>
@@ -1341,7 +1430,7 @@
<td>${item.chapter}</td>
<td>${item.pages}</td>
<td><span class="status-tag ${statusClass(item.status)}">${item.status}</span></td>
<td>${item.note}</td>
<td>${item.note}${activeConversation.productName ? `<br /><span class="muted">所属产品:${activeConversation.productName}</span>` : ""}</td>
</tr>
`).join("")}
</tbody>
@@ -1672,6 +1761,17 @@
return;
}
const openConversation = event.target.closest("[data-open-conversation]");
if (openConversation) {
data.activeConversationId = openConversation.dataset.openConversation;
activeTabId = "chat";
const currentConversation = getActiveConversation();
activeNodeId = currentConversation.nodes[0]?.id || "";
data.activeAbilityKey = Object.keys(currentConversation.abilities)[0] || "";
rerender();
return;
}
const node = event.target.closest("[data-node-id]");
if (node) {
activeNodeId = node.dataset.nodeId;
@@ -1689,6 +1789,15 @@
rerender();
}
});
document.addEventListener("input", event => {
if (event.target.id === "materialSearchInput") {
data.materialSearchQuery = event.target.value;
if (activeTabId === "materials") {
renderPages();
}
}
});
</script>
</body>
</html>