修复: - Document.to_dict() 添加 file_type 和 file_size 字段 - 文档列表正确显示类型和大小 新增: - /documents/<id> 文档详情页面 - document_detail.html 模板(显示文档信息、分块内容、关键词等)
212 lines
9.1 KiB
HTML
212 lines
9.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{ doc.title or doc.filename }} - 文档详情</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
<style>
|
|
body { background-color: #f8f9fa; }
|
|
.chunk-card { border-left: 4px solid #667eea; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
|
<div class="container">
|
|
<a class="navbar-brand" href="/"><i class="bi bi-search"></i> LLM Index RAG</a>
|
|
<div class="navbar-nav ms-auto">
|
|
<a class="nav-link" href="/">首页</a>
|
|
<a class="nav-link" href="/documents">文档管理</a>
|
|
<a class="nav-link" href="/search">知识检索</a>
|
|
<a class="nav-link" href="/settings">系统设置</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="container py-4">
|
|
<nav aria-label="breadcrumb" class="mb-3">
|
|
<ol class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="/documents">文档管理</a></li>
|
|
<li class="breadcrumb-item active">{{ doc.title or doc.filename }}</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<!-- 文档信息 -->
|
|
<div class="card mb-4">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0"><i class="bi bi-file-earmark-text"></i> 文档信息</h5>
|
|
<div>
|
|
{% if doc.status == 'indexed' %}
|
|
<span class="badge bg-success">已索引</span>
|
|
{% elif doc.status == 'processing' %}
|
|
<span class="badge bg-warning">处理中</span>
|
|
{% elif doc.status == 'failed' %}
|
|
<span class="badge bg-danger">失败</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">待索引</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<table class="table table-sm">
|
|
<tr>
|
|
<th width="120">文件名</th>
|
|
<td>{{ doc.filename }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>标题</th>
|
|
<td>{{ doc.title or '-' }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>文件类型</th>
|
|
<td>{{ doc.file_type }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>文件大小</th>
|
|
<td>{{ (doc.file_size / 1024)|round(1) }} KB</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<table class="table table-sm">
|
|
<tr>
|
|
<th width="120">分块数</th>
|
|
<td>{{ doc.chunk_count }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>字数</th>
|
|
<td>{{ doc.word_count|default(0) }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>上传时间</th>
|
|
<td>{{ doc.created_at.strftime('%Y-%m-%d %H:%M') if doc.created_at else '-' }}</td>
|
|
</tr>
|
|
<tr>
|
|
<th>索引时间</th>
|
|
<td>{{ doc.indexed_at.strftime('%Y-%m-%d %H:%M') if doc.indexed_at else '-' }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{% if doc.summary %}
|
|
<div class="mt-3">
|
|
<h6><i class="bi bi-card-text"></i> 文档摘要</h6>
|
|
<p class="text-muted">{{ doc.summary }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if doc.keywords %}
|
|
<div class="mt-3">
|
|
<h6><i class="bi bi-tags"></i> 关键词</h6>
|
|
<div>
|
|
{% for kw in doc.get_keywords() %}
|
|
<span class="badge bg-primary me-1">{{ kw }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if doc.category %}
|
|
<div class="mt-3">
|
|
<h6><i class="bi bi-folder"></i> 分类</h6>
|
|
<span class="badge bg-info">{{ doc.category }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 文档分块 -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0"><i class="bi bi-puzzle"></i> 文档分块 ({{ chunks|length }} 个)</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if chunks %}
|
|
<div class="accordion" id="chunksAccordion">
|
|
{% for chunk in chunks %}
|
|
<div class="accordion-item">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#chunk{{ loop.index }}">
|
|
<strong>分块 {{ loop.index }}</strong>
|
|
<span class="ms-3 text-muted small">
|
|
{{ chunk.content[:50] }}...
|
|
</span>
|
|
</button>
|
|
</h2>
|
|
<div id="chunk{{ loop.index }}" class="accordion-collapse collapse" data-bs-parent="#chunksAccordion">
|
|
<div class="accordion-body">
|
|
{% if chunk.summary %}
|
|
<div class="alert alert-info small">
|
|
<strong>摘要:</strong> {{ chunk.summary }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if chunk.keywords %}
|
|
<div class="mb-2">
|
|
{% for kw in chunk.get_keywords() %}
|
|
<span class="badge bg-secondary me-1">{{ kw }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<pre class="bg-light p-3 rounded" style="white-space: pre-wrap; word-wrap: break-word;">{{ chunk.content }}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="text-muted text-center py-4">暂无分块数据,请先索引文档</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<a href="/documents" class="btn btn-secondary">
|
|
<i class="bi bi-arrow-left"></i> 返回列表
|
|
</a>
|
|
{% if doc.status == 'pending' %}
|
|
<button class="btn btn-primary" onclick="indexDoc({{ doc.id }})">
|
|
<i class="bi bi-arrow-repeat"></i> 索引文档
|
|
</button>
|
|
{% endif %}
|
|
<button class="btn btn-danger" onclick="deleteDoc({{ doc.id }})">
|
|
<i class="bi bi-trash"></i> 删除文档
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
function indexDoc(id) {
|
|
if (!confirm('确定要索引此文档吗?')) return;
|
|
|
|
fetch(`/api/index/${id}`, { method: 'POST' })
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
alert(data.success ? '索引完成' : '索引失败: ' + (data.error || '未知错误'));
|
|
if (data.success) location.reload();
|
|
});
|
|
}
|
|
|
|
function deleteDoc(id) {
|
|
if (!confirm('确定要删除此文档吗?此操作不可恢复!')) return;
|
|
|
|
fetch(`/api/documents/${id}`, { method: 'DELETE' })
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('删除成功');
|
|
window.location.href = '/documents';
|
|
} else {
|
|
alert('删除失败');
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |