feat:
- 在 FileDetailModal 组件中,新增下载原文和下载 Markdown 按钮。 - 实现下载原文和 Markdown 的逻辑,包括文件名处理和错误提示。 - 优化模态框标题样式,增强用户体验。
This commit is contained in:
parent
2f64a4b597
commit
47bf8f41fe
@ -19,6 +19,7 @@
|
||||
### 新增
|
||||
- 优化知识库详情页面,更加简洁清晰
|
||||
- 新增对于上传文件的智能体中间件
|
||||
- 增强文件下载功能
|
||||
|
||||
### 修复
|
||||
- 修复重排序模型实际未生效的问题
|
||||
|
||||
@ -1,13 +1,39 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="file?.filename || '文件详情'"
|
||||
width="1200px"
|
||||
:footer="null"
|
||||
wrap-class-name="file-detail"
|
||||
@after-open-change="afterOpenChange"
|
||||
:bodyStyle="{ height: '80vh', padding: '0' }"
|
||||
>
|
||||
<template #title>
|
||||
<div class="modal-title-wrapper">
|
||||
<span>{{ file?.filename || '文件详情' }}</span>
|
||||
<div class="download-buttons" v-if="file">
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleDownloadOriginal"
|
||||
:loading="downloadingOriginal"
|
||||
:disabled="!file.file_id"
|
||||
:icon="h(DownloadOutlined)"
|
||||
>
|
||||
下载原文
|
||||
</a-button>
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleDownloadMarkdown"
|
||||
:loading="downloadingMarkdown"
|
||||
:disabled="!file.lines || file.lines.length === 0"
|
||||
:icon="h(DownloadOutlined)"
|
||||
>
|
||||
下载 Markdown
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="file-detail-content" v-if="file">
|
||||
<div class="file-content-section" v-if="file.lines && file.lines.length > 0">
|
||||
<MarkdownContentViewer :chunks="file.lines" />
|
||||
@ -25,8 +51,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref, h } from 'vue';
|
||||
import { useDatabaseStore } from '@/stores/database';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { DownloadOutlined } from '@ant-design/icons-vue';
|
||||
import { documentApi } from '@/apis/knowledge_api';
|
||||
import { mergeChunks } from '@/utils/chunkUtils';
|
||||
import MarkdownContentViewer from './MarkdownContentViewer.vue';
|
||||
|
||||
const store = useDatabaseStore();
|
||||
|
||||
@ -37,6 +68,8 @@ const visible = computed({
|
||||
|
||||
const file = computed(() => store.selectedFile);
|
||||
const loading = computed(() => store.state.fileDetailLoading);
|
||||
const downloadingOriginal = ref(false);
|
||||
const downloadingMarkdown = ref(false);
|
||||
|
||||
const afterOpenChange = (open) => {
|
||||
if (!open) {
|
||||
@ -44,9 +77,113 @@ const afterOpenChange = (open) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 导入工具函数
|
||||
import { getStatusText, formatStandardTime } from '@/utils/file_utils';
|
||||
import MarkdownContentViewer from './MarkdownContentViewer.vue';
|
||||
// 下载原文
|
||||
const handleDownloadOriginal = async () => {
|
||||
if (!file.value || !file.value.file_id) {
|
||||
message.error('文件信息不完整');
|
||||
return;
|
||||
}
|
||||
|
||||
const dbId = store.databaseId;
|
||||
if (!dbId) {
|
||||
message.error('无法获取数据库ID,请刷新页面后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
downloadingOriginal.value = true;
|
||||
try {
|
||||
const response = await documentApi.downloadDocument(dbId, file.value.file_id);
|
||||
|
||||
// 获取文件名
|
||||
const contentDisposition = response.headers.get('content-disposition');
|
||||
let filename = file.value.filename;
|
||||
if (contentDisposition) {
|
||||
// 首先尝试匹配RFC 2231格式 filename*=UTF-8''...
|
||||
const rfc2231Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/);
|
||||
if (rfc2231Match) {
|
||||
try {
|
||||
filename = decodeURIComponent(rfc2231Match[1]);
|
||||
} catch (error) {
|
||||
console.warn('Failed to decode RFC2231 filename:', rfc2231Match[1], error);
|
||||
}
|
||||
} else {
|
||||
// 回退到标准格式 filename="..."
|
||||
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
||||
if (filenameMatch && filenameMatch[1]) {
|
||||
filename = filenameMatch[1].replace(/['"]/g, '');
|
||||
// 解码URL编码的文件名
|
||||
try {
|
||||
filename = decodeURIComponent(filename);
|
||||
} catch (error) {
|
||||
console.warn('Failed to decode filename:', filename, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建blob并下载
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
message.success('下载成功');
|
||||
} catch (error) {
|
||||
console.error('下载文件时出错:', error);
|
||||
message.error(error.message || '下载文件失败');
|
||||
} finally {
|
||||
downloadingOriginal.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 下载 Markdown
|
||||
const handleDownloadMarkdown = () => {
|
||||
if (!file.value || !file.value.lines || file.value.lines.length === 0) {
|
||||
message.error('没有可下载的 Markdown 内容');
|
||||
return;
|
||||
}
|
||||
|
||||
downloadingMarkdown.value = true;
|
||||
try {
|
||||
// 合并 chunks 生成完整的 Markdown 内容
|
||||
const { content } = mergeChunks(file.value.lines);
|
||||
|
||||
// 生成文件名(如果原文件没有 .md 扩展名,则添加)
|
||||
let filename = file.value.filename || 'document.md';
|
||||
if (!filename.toLowerCase().endsWith('.md')) {
|
||||
// 移除原扩展名,添加 .md
|
||||
const lastDotIndex = filename.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
filename = filename.substring(0, lastDotIndex) + '.md';
|
||||
} else {
|
||||
filename = filename + '.md';
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 blob 并下载
|
||||
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
message.success('下载成功');
|
||||
} catch (error) {
|
||||
console.error('下载 Markdown 时出错:', error);
|
||||
message.error(error.message || '下载 Markdown 失败');
|
||||
} finally {
|
||||
downloadingMarkdown.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@ -82,5 +219,26 @@ import MarkdownContentViewer from './MarkdownContentViewer.vue';
|
||||
.ant-modal {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
.ant-modal-title {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-title-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.download-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin: 0 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user