feat: 优化知识库和文件详情展示

- 在知识库中简化了文件加载逻辑,移除了不必要的节点加载。
- 将文件详情展示从抽屉组件改为模态框,增强用户体验。
- 添加了加载状态指示,改善了文件详情的展示效果。
- 新增分页功能,提升了文件列表的可用性。
This commit is contained in:
Wenjie Zhang 2025-05-26 13:59:05 +08:00
parent d9802414f1
commit 9d2604801d
2 changed files with 72 additions and 25 deletions

View File

@ -106,7 +106,7 @@ class KnowledgeBase:
"""根据ID获取知识库""" """根据ID获取知识库"""
with db_manager.get_session_context() as session: with db_manager.get_session_context() as session:
db = session.query(KnowledgeDatabase).options( db = session.query(KnowledgeDatabase).options(
joinedload(KnowledgeDatabase.files).joinedload(KnowledgeFile.nodes) joinedload(KnowledgeDatabase.files)
).filter_by(db_id=db_id).first() ).filter_by(db_id=db_id).first()
return db.to_dict() if db else None # Assuming to_dict handles files and nodes return db.to_dict() if db else None # Assuming to_dict handles files and nodes

View File

@ -135,7 +135,7 @@
</div> </div>
</div> </div>
</div> </div>
<a-table :columns="columns" :data-source="Object.values(database.files || {})" row-key="file_id" class="my-table"> <a-table :columns="columns" :data-source="Object.values(database.files || {})" row-key="file_id" class="my-table" size="small" bordered :pagination="pagination" >
<template #bodyCell="{ column, text, record }"> <template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'filename'"> <template v-if="column.key === 'filename'">
<a-tooltip :title="record.file_id" placement="right"> <a-tooltip :title="record.file_id" placement="right">
@ -183,19 +183,28 @@
<span v-else>{{ text }}</span> <span v-else>{{ text }}</span>
</template> </template>
</a-table> </a-table>
<a-drawer <a-modal
width="50%" v-model:open="state.fileDetailModalVisible"
v-model:open="state.drawer"
class="custom-class" class="custom-class"
:title="selectedFile?.filename || '文件详情'" :title="selectedFile?.filename || '文件详情'"
placement="right" width="1000px"
@after-open-change="afterOpenChange" @after-open="afterOpenChange"
:footer="null"
> >
<h2> {{ selectedFile?.lines?.length || 0 }} 个片段</h2> <template v-if="state.fileDetailLoading">
<p v-for="line in selectedFile?.lines || []" :key="line.id" class="line-text"> <div class="loading-container">
{{ line.text }} <a-spin tip="加载中..." />
</p> </div>
</a-drawer> </template>
<template v-else>
<h2> {{ selectedFile?.lines?.length || 0 }} 个片段</h2>
<div class="file-detail-content">
<p v-for="line in selectedFile?.lines || []" :key="line.id" class="line-text">
{{ line.text }}
</p>
</div>
</template>
</a-modal>
</div> </div>
</a-tab-pane> </a-tab-pane>
@ -364,7 +373,8 @@ const state = reactive({
refrashing: false, refrashing: false,
searchLoading: false, searchLoading: false,
lock: false, lock: false,
drawer: false, fileDetailModalVisible: false,
fileDetailLoading: false,
refreshInterval: null, refreshInterval: null,
curPage: "files", curPage: "files",
indexingFile: null, indexingFile: null,
@ -394,6 +404,13 @@ const use_rewrite_queryOptions = ref([
{ value: 'hyde', payload: { title: 'hyde', subTitle: '伪文档生成' } }, { value: 'hyde', payload: { title: 'hyde', subTitle: '伪文档生成' } },
]) ])
const pagination = ref({
pageSize: 30,
current: 1,
total: 0,
showTotal: (total, range) => `${total}`,
})
const filterQueryResults = () => { const filterQueryResults = () => {
if (!queryResult.value || !queryResult.value.all_results) { if (!queryResult.value || !queryResult.value.all_results) {
return; return;
@ -531,33 +548,49 @@ const deleteDatabse = () => {
} }
const openFileDetail = (record) => { const openFileDetail = (record) => {
state.lock = true //
state.fileDetailModalVisible = true;
selectedFile.value = {
...record,
lines: []
};
//
state.fileDetailLoading = true;
state.lock = true;
try { try {
knowledgeBaseApi.getDocumentDetail(databaseId.value, record.file_id) knowledgeBaseApi.getDocumentDetail(databaseId.value, record.file_id)
.then(data => { .then(data => {
console.log(data) console.log(data);
if (data.status == "failed") { if (data.status == "failed") {
message.error(data.message) message.error(data.message);
return state.fileDetailModalVisible = false;
return;
} }
state.lock = false
selectedFile.value = { selectedFile.value = {
...record, ...record,
lines: data.lines || [] lines: data.lines || []
} };
state.drawer = true
}) })
.catch(error => { .catch(error => {
console.error(error) console.error(error);
message.error(error.message) message.error(error.message);
state.fileDetailModalVisible = false;
}) })
.finally(() => {
state.fileDetailLoading = false;
state.lock = false;
});
} catch (error) { } catch (error) {
console.error(error) console.error(error);
message.error('获取文件详情失败') message.error('获取文件详情失败');
state.lock = false state.fileDetailLoading = false;
state.lock = false;
state.fileDetailModalVisible = false;
} }
} }
const formatRelativeTime = (timestamp) => { const formatRelativeTime = (timestamp) => {
// UTC+8 // UTC+8
const timezoneOffset = 8 * 60 * 60 * 1000; // const timezoneOffset = 8 * 60 * 60 * 1000; //
@ -1245,9 +1278,16 @@ const handleIndexFile = (fileId) => {
} }
} }
.file-detail-content {
max-height: 60vh;
overflow-y: auto;
padding: 0 10px;
}
.custom-class .line-text { .custom-class .line-text {
padding: 10px; padding: 10px;
border-radius: 4px; border-radius: 4px;
margin: 8px 0;
&:hover { &:hover {
background-color: var(--main-light-4); background-color: var(--main-light-4);
@ -1410,6 +1450,13 @@ const handleIndexFile = (fileId) => {
margin-top: 5px; margin-top: 5px;
line-height: 1.5; line-height: 1.5;
} }
.loading-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
}
</style> </style>
<style lang="less"> <style lang="less">