857 lines
30 KiB
HTML
857 lines
30 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}审核智能体{% endblock %}
|
||
{% block page_class %}chat-page{% endblock %}
|
||
|
||
{% block content %}
|
||
<style>
|
||
.chat-page {
|
||
width: min(100vw - 32px, 1920px);
|
||
margin-top: 12px;
|
||
gap: 12px;
|
||
}
|
||
|
||
.chat-hero {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
min-height: 74px;
|
||
padding: 16px 20px;
|
||
}
|
||
|
||
.chat-hero .page-title {
|
||
font-size: 1.45rem;
|
||
}
|
||
|
||
.chat-layout {
|
||
display: grid;
|
||
grid-template-columns: 280px minmax(680px, 1fr) 340px;
|
||
gap: 12px;
|
||
align-items: start;
|
||
}
|
||
|
||
.history-toggle {
|
||
position: absolute;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.chat-layout:has(.history-toggle:checked) {
|
||
grid-template-columns: 54px minmax(760px, 1fr) 340px;
|
||
}
|
||
|
||
.chat-layout:has(.history-toggle:checked) .history-content {
|
||
display: none;
|
||
}
|
||
|
||
.chat-layout:has(.history-toggle:checked) .history-panel {
|
||
padding: 12px 8px;
|
||
}
|
||
|
||
.chat-layout:has(.history-toggle:checked) .history-collapse {
|
||
width: 36px;
|
||
height: 36px;
|
||
padding: 0;
|
||
}
|
||
|
||
.chat-layout:has(.history-toggle:checked) .history-collapse span {
|
||
display: none;
|
||
}
|
||
|
||
.history-panel,
|
||
.right-rail {
|
||
position: sticky;
|
||
top: 84px;
|
||
}
|
||
|
||
.history-head,
|
||
.right-rail-head,
|
||
.chat-head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.history-collapse {
|
||
flex: none;
|
||
min-height: 34px;
|
||
border-radius: 8px;
|
||
font-size: 0.86rem;
|
||
background: var(--surface-soft);
|
||
color: var(--muted);
|
||
}
|
||
|
||
.history-list {
|
||
max-height: calc(100vh - 230px);
|
||
overflow: auto;
|
||
padding-right: 2px;
|
||
}
|
||
|
||
.history-card {
|
||
border-radius: 10px;
|
||
background: #fff;
|
||
}
|
||
|
||
.chat-shell {
|
||
min-height: calc(100vh - 190px);
|
||
display: grid;
|
||
grid-template-rows: auto minmax(360px, 1fr) auto;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.chat-head {
|
||
padding: 16px 18px 12px;
|
||
border-bottom: 1px solid var(--border);
|
||
margin: 0;
|
||
}
|
||
|
||
.node-strip {
|
||
display: flex;
|
||
gap: 8px;
|
||
overflow-x: auto;
|
||
padding-bottom: 2px;
|
||
max-width: 52vw;
|
||
}
|
||
|
||
.chat-thread {
|
||
display: grid;
|
||
align-content: start;
|
||
gap: 18px;
|
||
padding: 22px min(7vw, 72px);
|
||
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
||
overflow: auto;
|
||
}
|
||
|
||
.chat-message {
|
||
display: grid;
|
||
grid-template-columns: 34px minmax(0, 1fr);
|
||
gap: 12px;
|
||
max-width: 980px;
|
||
}
|
||
|
||
.chat-message.user {
|
||
justify-self: end;
|
||
grid-template-columns: minmax(0, 1fr) 34px;
|
||
}
|
||
|
||
.avatar {
|
||
width: 34px;
|
||
height: 34px;
|
||
border-radius: 10px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 700;
|
||
font-size: 0.82rem;
|
||
background: var(--primary);
|
||
color: #fff;
|
||
}
|
||
|
||
.chat-message.user .avatar {
|
||
grid-column: 2;
|
||
background: #111827;
|
||
}
|
||
|
||
.bubble {
|
||
padding: 13px 15px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
background: #fff;
|
||
line-height: 1.75;
|
||
box-shadow: 0 4px 14px rgba(31, 45, 61, 0.04);
|
||
}
|
||
|
||
.chat-message.user .bubble {
|
||
grid-column: 1;
|
||
grid-row: 1;
|
||
background: #eef4ff;
|
||
border-color: #d8e5ff;
|
||
}
|
||
|
||
.bubble-meta {
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
color: var(--muted);
|
||
font-size: 0.78rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.prompt-chips {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.composer {
|
||
padding: 14px min(7vw, 72px) 18px;
|
||
border-top: 1px solid var(--border);
|
||
background: rgba(255, 255, 255, 0.96);
|
||
}
|
||
|
||
.composer-box {
|
||
display: grid;
|
||
gap: 10px;
|
||
max-width: 1040px;
|
||
margin: 0 auto;
|
||
border: 1px solid var(--border);
|
||
border-radius: 16px;
|
||
background: #fff;
|
||
padding: 10px;
|
||
box-shadow: 0 10px 28px rgba(31, 45, 61, 0.08);
|
||
}
|
||
|
||
.composer-box label {
|
||
display: none;
|
||
}
|
||
|
||
.composer-box textarea {
|
||
height: 86px;
|
||
min-height: 78px;
|
||
max-height: 180px;
|
||
border: 0;
|
||
padding: 8px 10px;
|
||
resize: vertical;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.composer-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
border-top: 1px solid #edf1f5;
|
||
padding-top: 10px;
|
||
}
|
||
|
||
.scope-summary {
|
||
color: var(--muted);
|
||
font-size: 0.86rem;
|
||
}
|
||
|
||
.document-scope {
|
||
display: none;
|
||
padding: 10px;
|
||
border-radius: 10px;
|
||
background: var(--surface-soft);
|
||
}
|
||
|
||
.composer-box:focus-within .document-scope {
|
||
display: block;
|
||
}
|
||
|
||
.send-button {
|
||
border-radius: 999px;
|
||
min-width: 92px;
|
||
}
|
||
|
||
.result-panel {
|
||
padding: 14px 18px;
|
||
}
|
||
|
||
.upload-card {
|
||
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
|
||
}
|
||
|
||
.upload-dropzone {
|
||
position: relative;
|
||
display: grid;
|
||
place-items: center;
|
||
min-height: 164px;
|
||
padding: 18px;
|
||
border: 1.5px dashed #9fb7e8;
|
||
border-radius: 12px;
|
||
background: #f4f8ff;
|
||
text-align: center;
|
||
color: var(--text);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.upload-dropzone input[type="file"] {
|
||
position: absolute;
|
||
inset: 0;
|
||
opacity: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.upload-icon {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 12px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto 10px;
|
||
background: #ffffff;
|
||
color: var(--primary);
|
||
box-shadow: 0 6px 18px rgba(47, 111, 236, 0.14);
|
||
font-size: 1.3rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.upload-dropzone strong {
|
||
display: block;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.rail-card {
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
background: #fff;
|
||
padding: 12px;
|
||
}
|
||
|
||
.status-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 10px;
|
||
}
|
||
|
||
.status-tile {
|
||
min-height: 74px;
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 10px;
|
||
background: var(--surface-soft);
|
||
}
|
||
|
||
.status-tile strong {
|
||
display: block;
|
||
margin-bottom: 6px;
|
||
font-size: 0.82rem;
|
||
}
|
||
|
||
@media (max-width: 1200px) {
|
||
.chat-layout,
|
||
.chat-layout:has(.history-toggle:checked) {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
.history-panel,
|
||
.right-rail {
|
||
position: static;
|
||
}
|
||
.node-strip {
|
||
max-width: 100%;
|
||
}
|
||
.chat-thread,
|
||
.composer {
|
||
padding-left: 18px;
|
||
padding-right: 18px;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<section class="page-header chat-hero">
|
||
<div>
|
||
<span class="eyebrow">Agent Workspace</span>
|
||
<h1 class="page-title">审核智能体</h1>
|
||
<p class="page-lead">围绕会话完成知识库问答、资料包上传、节点审核和导出留痕。</p>
|
||
</div>
|
||
{% if conversation %}
|
||
<div class="badge-row">
|
||
<span class="pill pill-accent">批次:{{ conversation.batch_id|default:"未绑定" }}</span>
|
||
<span class="pill">产品:{{ conversation.product_name|default:"知识库问答" }}</span>
|
||
<span class="pill">阶段:{{ conversation_context.task_status }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</section>
|
||
|
||
<section class="chat-layout">
|
||
<input class="history-toggle" id="history-collapsed" type="checkbox">
|
||
<aside class="panel history-panel">
|
||
<div class="history-head">
|
||
<div class="history-content">
|
||
<h2 class="section-title">会话历史</h2>
|
||
<p class="section-copy">按会话快速切换资料包和知识库问答。</p>
|
||
</div>
|
||
<label class="button history-collapse" for="history-collapsed" title="收起或展开会话历史">‹ <span>收起</span></label>
|
||
</div>
|
||
<div class="history-content">
|
||
<ul class="detail-list history-list">
|
||
{% for item in conversation_history %}
|
||
<li class="detail-item history-card">
|
||
<strong><a href="{% url 'chat:detail' item.conversation_id %}">{{ item.title }}</a></strong>
|
||
<div class="muted">产品:{{ item.product_name|default:"未识别" }}</div>
|
||
<div class="muted">批次:{{ item.batch_id|default:"未绑定" }}</div>
|
||
<div class="muted">风险:{{ item.risk_level }}</div>
|
||
<div class="muted">最近更新:{{ item.updated_at|date:"Y-m-d H:i" }}</div>
|
||
<div class="badge-row" style="margin-top: 8px;">
|
||
<span class="pill pill-accent">{{ item.batch_binding_label }}</span>
|
||
</div>
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">
|
||
<strong>暂无历史会话</strong>
|
||
<div class="muted">可以直接在中间区域提问,Agent 会优先检索知识库。</div>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
</aside>
|
||
|
||
<div class="stack">
|
||
<article class="panel chat-shell">
|
||
<div class="chat-head">
|
||
<div>
|
||
<h2 class="section-title">对话区与节点导航</h2>
|
||
<p class="section-copy">像聊天一样提问,Agent 会结合知识库和所选文档回答。</p>
|
||
</div>
|
||
{% if node_results %}
|
||
<div class="node-strip">
|
||
{% for node in node_results %}
|
||
<span class="pill {% if node.status == '已完成' %}pill-success{% else %}pill-signal{% endif %}">{{ node.label }} / {{ node.status }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="chat-thread">
|
||
{% if conversation %}
|
||
<div class="chat-message assistant">
|
||
<div class="avatar">RA</div>
|
||
<div class="bubble">
|
||
<span class="bubble-meta">审核智能体</span>
|
||
当前会话已就绪。可以询问资料目录、法规依据、字段抽取、一致性问题或风险整改建议。
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="chat-message assistant">
|
||
<div class="avatar">RA</div>
|
||
<div class="bubble">
|
||
<span class="bubble-meta">审核智能体</span>
|
||
可以先不上传资料,直接询问注册法规、资料清单、模板字段或历史知识库内容。
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if form.message.value %}
|
||
<div class="chat-message user">
|
||
<div class="avatar">你</div>
|
||
<div class="bubble">
|
||
<span class="bubble-meta">用户问题</span>
|
||
{{ form.message.value|linebreaksbr }}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
{% if result %}
|
||
<div class="chat-message assistant">
|
||
<div class="avatar">RA</div>
|
||
<div class="bubble">
|
||
<span class="bubble-meta">Agent 回答</span>
|
||
{{ result.answer|linebreaksbr }}
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<div class="prompt-chips">
|
||
{% for item in prompt_templates %}
|
||
<span class="pill pill-accent">{{ item }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<form method="post" class="composer" action="{% if conversation %}{% url 'chat:detail' conversation.conversation_id %}{% else %}{% url 'chat:index' %}{% endif %}">
|
||
{% csrf_token %}
|
||
<div class="composer-box">
|
||
{{ form.message.label_tag }}
|
||
{{ form.message }}
|
||
{% if form.message.errors %}
|
||
<p class="notice notice-error">{{ form.message.errors|join:" " }}</p>
|
||
{% endif %}
|
||
<div class="document-scope">
|
||
{{ form.document_ids.label_tag }}
|
||
<div class="checkbox-list">
|
||
{% for checkbox in form.document_ids %}
|
||
<label class="checkbox-item">
|
||
{{ checkbox.tag }}
|
||
<span>{{ checkbox.choice_label }}</span>
|
||
</label>
|
||
{% empty %}
|
||
<div class="notice">未选择文档时,Agent 会按问题检索当前知识库。</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
<div class="composer-actions">
|
||
<span class="scope-summary">默认检索知识库;聚焦输入框可选择文档范围。</span>
|
||
<button class="send-button" type="submit">发送问题</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</article>
|
||
|
||
<article class="panel result-panel">
|
||
<h2 class="section-title">节点式结果</h2>
|
||
{% if analysis_card or export_card or risk_card or notify_card %}
|
||
<div class="stack">
|
||
{% if analysis_card %}
|
||
<div class="detail-item">
|
||
<strong>{{ analysis_card.title }}</strong>
|
||
{% if analysis_card.summary %}
|
||
<div>{{ analysis_card.summary }}</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">摘要指标</h3>
|
||
<div class="badge-row">
|
||
{% for item in analysis_card.stats %}
|
||
<span class="pill pill-accent">{{ item.label }}:{{ item.value }}</span>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">节点结果明细</h3>
|
||
<ul class="detail-list">
|
||
{% if analysis_card.kind == "overview" %}
|
||
{% for item in analysis_card.items %}
|
||
<li class="detail-item">{{ item.chapter_code }} / {{ item.document_count }} 份</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无目录汇总结果。</li>
|
||
{% endfor %}
|
||
{% elif analysis_card.kind == "completeness" %}
|
||
{% for item in analysis_card.items %}
|
||
<li class="detail-item">
|
||
<strong>{{ item.document_name|default:"必交项" }}</strong>
|
||
<div>章节点:{{ item.chapter_code|default:"-" }}</div>
|
||
<div>{{ item.reason|default:"当前资料包未提供该资料。" }}</div>
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无缺失项。</li>
|
||
{% endfor %}
|
||
{% elif analysis_card.kind == "consistency" %}
|
||
{% for item in analysis_card.items %}
|
||
<li class="detail-item">
|
||
<strong>{{ item.field_name|default:"冲突字段" }}</strong>
|
||
<div>{{ item.issue|default:item }}</div>
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无一致性冲突。</li>
|
||
{% endfor %}
|
||
{% elif analysis_card.kind == "field_extraction" %}
|
||
{% for item in analysis_card.items %}
|
||
<li class="detail-item">
|
||
<strong>{{ item.field_name|default:"抽取字段" }}</strong>
|
||
<div>{{ item.field_value|default:"-" }}</div>
|
||
{% if item.source_document %}
|
||
<div class="muted">来源:{{ item.source_document }}</div>
|
||
{% endif %}
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无抽取字段。</li>
|
||
{% endfor %}
|
||
{% else %}
|
||
{% for item in analysis_card.items %}
|
||
<li class="detail-item">{{ item }}</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无明细结果。</li>
|
||
{% endfor %}
|
||
{% endif %}
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">提示与异常</h3>
|
||
<ul class="detail-list">
|
||
{% for item in analysis_card.warnings %}
|
||
<li class="detail-item">
|
||
{% if analysis_card.kind == "field_extraction" %}
|
||
<strong>{{ item.field_name|default:"低置信度字段" }}</strong>
|
||
<div>{{ item.field_value|default:"-" }}</div>
|
||
{% if item.source_document %}
|
||
<div class="muted">来源:{{ item.source_document }}</div>
|
||
{% endif %}
|
||
{% elif analysis_card.kind == "completeness" %}
|
||
<strong>{{ item.document_name|default:"错放项" }}</strong>
|
||
<div>目标章节点:{{ item.chapter_code|default:"-" }}</div>
|
||
<div>当前归类:{{ item.current_location|default:"未识别" }}</div>
|
||
{% else %}
|
||
{{ item }}
|
||
{% endif %}
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无额外提示。</li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if risk_card %}
|
||
<div class="detail-item">
|
||
<strong>风险预警能力卡</strong>
|
||
<div>总风险等级:{{ risk_card.highest_risk_level|default:"-" }}</div>
|
||
<div>是否通过:{{ risk_card.pass_status|default:"-" }}</div>
|
||
{% if risk_card.summary %}
|
||
<div>{{ risk_card.summary }}</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">待复核与风险项</h3>
|
||
<ul class="detail-list">
|
||
{% for item in risk_card.manual_review_items %}
|
||
<li class="detail-item">{{ item }}</li>
|
||
{% empty %}
|
||
{% for item in risk_card.risk_items %}
|
||
<li class="detail-item">
|
||
<strong>{{ item.title|default:item.issue }}</strong>
|
||
<div class="muted">{{ item.risk_level|default:"-" }}</div>
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无待复核项。</li>
|
||
{% endfor %}
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">责任角色</h3>
|
||
<div class="table-wrap">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>owner_role</th>
|
||
<th>owner_name</th>
|
||
<th>department</th>
|
||
<th>chapter_scope</th>
|
||
<th>risk_scope</th>
|
||
<th>feishu_user_id</th>
|
||
<th>feishu_open_id</th>
|
||
<th>feishu_name</th>
|
||
<th>notify_enabled</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for item in risk_card.owner_roles %}
|
||
<tr>
|
||
<td>{{ item.owner_role }}</td>
|
||
<td>{{ item.owner_name }}</td>
|
||
<td>{{ item.department }}</td>
|
||
<td>{{ item.chapter_scope }}</td>
|
||
<td>{{ item.risk_scope }}</td>
|
||
<td>{{ item.feishu_user_id }}</td>
|
||
<td>{{ item.feishu_open_id }}</td>
|
||
<td>{{ item.feishu_name }}</td>
|
||
<td>{{ item.notify_enabled|yesno:"True,False" }}</td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr><td colspan="9">当前无责任角色。</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="detail-item">
|
||
{% if export_card %}
|
||
<strong>Word 导出能力卡</strong>
|
||
<div>模板:{{ export_card.template_name }} / {{ export_card.template_version|default:"-" }}</div>
|
||
<div>当前导出状态:{{ export_card.export_status|default:"-" }}</div>
|
||
{% elif notify_card %}
|
||
<strong>飞书通知能力卡</strong>
|
||
<div>通知原因:{{ notify_card.notify_reason|default:"-" }}</div>
|
||
<div>消息状态:{{ notify_card.message_status|default:"-" }}</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if export_card %}
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">回填字段表</h3>
|
||
<div class="table-wrap">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>占位符</th>
|
||
<th>字段名</th>
|
||
<th>字段值</th>
|
||
<th>来源</th>
|
||
<th>回填状态</th>
|
||
<th>是否必填</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for item in export_card.filled_fields %}
|
||
<tr>
|
||
<td>{{ item.placeholder }}</td>
|
||
<td>{{ item.field_name }}</td>
|
||
<td>{{ item.field_value }}</td>
|
||
<td>{{ item.source }}</td>
|
||
<td>{{ item.fill_status }}</td>
|
||
<td>{{ item.required|yesno:"是,否" }}</td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr><td colspan="6">当前暂无回填字段。</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if export_card %}
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">拦截项区</h3>
|
||
<ul class="detail-list">
|
||
{% for item in export_card.blocked_fields %}
|
||
<li class="detail-item">
|
||
<strong>{{ item.field_name }}</strong>
|
||
<div>拦截原因:{{ item.block_reason }}</div>
|
||
<div class="muted">来源:{{ item.risk_source }}</div>
|
||
</li>
|
||
{% empty %}
|
||
<li class="detail-item">当前无拦截项。</li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if notify_card %}
|
||
<div class="panel" style="padding: 14px;">
|
||
<h3 class="section-title" style="font-size: 1rem;">飞书通知能力卡</h3>
|
||
<ul class="detail-list">
|
||
<li class="detail-item"><strong>通知原因</strong><div>{{ notify_card.notify_reason|default:"-" }}</div></li>
|
||
<li class="detail-item"><strong>消息状态</strong><div>{{ notify_card.message_status|default:"-" }}</div></li>
|
||
<li class="detail-item"><strong>被 @ 处理人</strong><div>{{ notify_card.mentioned_users|join:" / "|default:"-" }}</div></li>
|
||
<li class="detail-item"><strong>Web 详情链接</strong><div>{{ notify_card.web_detail_url|default:"-" }}</div></li>
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="button-row">
|
||
{% if export_card %}
|
||
<a class="button" href="{% url 'platform_ui:knowledge-base' %}?view=template_mappings">维护 Word 模板</a>
|
||
<a class="button" href="{% url 'platform_ui:knowledge-base' %}?view=field_schemas">维护字段映射</a>
|
||
<a class="button" href="{% url 'audit:list' %}">查看导出记录</a>
|
||
{% endif %}
|
||
{% if risk_card %}
|
||
<a class="button" href="{% url 'platform_ui:knowledge-base' %}?view=owner_mappings">查看责任人映射</a>
|
||
{% endif %}
|
||
{% if notify_card %}
|
||
<a class="button" href="{% url 'platform_ui:knowledge-base' %}?view=feishu_configs">查看飞书配置</a>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% elif result and result.structured_output %}
|
||
<table class="kv-table">
|
||
<tbody>
|
||
{% for key, value in result.structured_output.items %}
|
||
<tr>
|
||
<th>{{ key }}</th>
|
||
<td><pre class="code-block">{{ value }}</pre></td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
{% else %}
|
||
<div class="notice">执行任务后,这里会展示结构化节点结果。</div>
|
||
{% endif %}
|
||
</article>
|
||
</div>
|
||
|
||
<aside class="stack right-rail">
|
||
<article class="panel upload-card">
|
||
<div class="right-rail-head">
|
||
<div>
|
||
<h2 class="section-title">上传区</h2>
|
||
<p class="section-copy">拖拽或点击导入资料包。</p>
|
||
</div>
|
||
</div>
|
||
{% if batch %}
|
||
<ul class="detail-list" style="margin-bottom: 14px;">
|
||
<li class="detail-item rail-card">
|
||
<strong>当前资料包</strong>
|
||
<div>批次:{{ batch.batch_id }}</div>
|
||
<div>文件数:{{ batch.file_count }}</div>
|
||
<div>页数:{{ batch.page_count }}</div>
|
||
<div>导入状态:{{ batch.get_import_status_display_text }}</div>
|
||
</li>
|
||
</ul>
|
||
<form method="post" action="{% url 'chat:upload-documents' conversation.conversation_id %}" enctype="multipart/form-data" class="stack" style="margin-top: 16px;">
|
||
{% csrf_token %}
|
||
<label class="upload-dropzone">
|
||
{{ upload_form.files }}
|
||
<span>
|
||
<span class="upload-icon">↑</span>
|
||
<strong>拖拽补充资料到这里</strong>
|
||
<span class="muted">或点击选择文件,支持多选和压缩包。</span>
|
||
</span>
|
||
</label>
|
||
<div class="button-row">
|
||
<button type="submit">继续上传资料</button>
|
||
<a class="button" href="{% url 'documents:list' %}">返回资料包</a>
|
||
</div>
|
||
</form>
|
||
<div class="button-row" style="margin-top: 16px;">
|
||
<a class="button" href="{% url 'documents:upload' %}">导入新资料包</a>
|
||
</div>
|
||
<form method="post" action="{% url 'chat:export-word' conversation.conversation_id %}" class="stack" style="margin-top: 16px;">
|
||
{% csrf_token %}
|
||
<div class="button-row">
|
||
<button type="submit">生成导出草稿</button>
|
||
</div>
|
||
</form>
|
||
{% else %}
|
||
<div class="notice">暂无绑定资料包,仍可先通过中间对话区查询知识库。</div>
|
||
<form method="post" action="{% url 'documents:upload' %}" enctype="multipart/form-data" class="stack" style="margin-top: 16px;">
|
||
{% csrf_token %}
|
||
<input type="hidden" name="scenario_id" value="document_review">
|
||
<label class="upload-dropzone">
|
||
{{ upload_form.files }}
|
||
<span>
|
||
<span class="upload-icon">↑</span>
|
||
<strong>拖拽资料包到这里</strong>
|
||
<span class="muted">PDF、DOCX、MD、TXT、ZIP、7Z、RAR 均可。</span>
|
||
</span>
|
||
</label>
|
||
<div class="button-row">
|
||
<button type="submit">导入资料包</button>
|
||
<a class="button" href="{% url 'documents:upload' %}">打开导入向导</a>
|
||
</div>
|
||
</form>
|
||
{% endif %}
|
||
</article>
|
||
|
||
<article class="panel">
|
||
<h2 class="section-title">动态信息卡</h2>
|
||
<div class="status-grid">
|
||
<div class="status-tile">
|
||
<strong>最高风险等级</strong>
|
||
<div>{{ workspace_summary.highest_risk_level }}</div>
|
||
</div>
|
||
<div class="status-tile">
|
||
<strong>是否允许正式导出</strong>
|
||
<div>{{ workspace_summary.export_allowed }}</div>
|
||
</div>
|
||
<div class="status-tile">
|
||
<strong>通知状态</strong>
|
||
<div>{{ workspace_summary.notify_status }}</div>
|
||
</div>
|
||
<div class="status-tile">
|
||
<strong>导出状态</strong>
|
||
<div>{{ workspace_summary.export_status }}</div>
|
||
</div>
|
||
</div>
|
||
<ul class="detail-list" style="margin-top: 10px;">
|
||
<li class="detail-item rail-card">
|
||
<strong>导出下载地址</strong>
|
||
<div>
|
||
{% if workspace_summary.download_url %}
|
||
<a href="{{ workspace_summary.download_url }}">下载导出文件</a>
|
||
{% else %}
|
||
-
|
||
{% endif %}
|
||
</div>
|
||
</li>
|
||
<li class="detail-item">当前会话围绕 conversation_id / batch_id / product_name 串联;未绑定资料包时以知识库问答方式执行。</li>
|
||
{% if audit_log %}
|
||
<li class="detail-item"><a href="{% url 'audit:detail' audit_log.id %}">查看本次处理历史</a></li>
|
||
{% endif %}
|
||
</ul>
|
||
</article>
|
||
</aside>
|
||
</section>
|
||
{% endblock %}
|