优化报告导出功能和Vditor显示
- 实现自定义导出格式选择框,向上延展显示 - 修复Vditor导出功能,使用正确的按钮选择器 - 添加导出后Vditor内容自动恢复机制 - 优化用户体验,支持PDF、HTML、Markdown、纯文本导出 - 添加详细的调试日志和错误处理 - 修复导出后Vditor内容消失的问题
This commit is contained in:
parent
b635c1fd08
commit
e7ea6b98c8
|
|
@ -122,16 +122,40 @@
|
|||
<!-- 操作按钮 -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-3">操作</h3>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<button
|
||||
@click="downloadReport"
|
||||
class="flex items-center gap-2 bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-colors"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 0 1 2-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
下载报告
|
||||
</button>
|
||||
<div class="flex flex-wrap gap-3 relative">
|
||||
<!-- 下载按钮 -->
|
||||
<div class="relative export-menu-container">
|
||||
<button
|
||||
@click="toggleExportMenu"
|
||||
class="flex items-center gap-2 bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-colors"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 0 1 2-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
下载报告
|
||||
<svg class="w-4 h-4 transition-transform duration-200" :class="{ 'rotate-180': showExportMenu }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- 导出格式选择菜单 -->
|
||||
<div
|
||||
v-if="showExportMenu"
|
||||
class="absolute bottom-full left-0 mb-1 bg-white border border-gray-200 rounded-lg shadow-lg z-50 min-w-[160px]"
|
||||
>
|
||||
<div class="py-1">
|
||||
<button
|
||||
v-for="format in exportFormats"
|
||||
:key="format.key"
|
||||
@click="exportReport(format.key)"
|
||||
class="w-full flex items-center gap-3 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<span class="text-lg">{{ format.icon }}</span>
|
||||
<span>{{ format.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click="shareReport"
|
||||
|
|
@ -163,7 +187,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onUnmounted, nextTick } from 'vue'
|
||||
import { ref, watch, onUnmounted, onMounted, nextTick } from 'vue'
|
||||
import Vditor from 'vditor'
|
||||
|
||||
// Props定义
|
||||
|
|
@ -182,6 +206,15 @@ const emit = defineEmits(['close', 'navigate', 'get-task-title'])
|
|||
// Vditor实例
|
||||
const vditorInstance = ref<Vditor | null>(null)
|
||||
|
||||
// 导出格式选择框状态
|
||||
const showExportMenu = ref(false)
|
||||
const exportFormats = [
|
||||
{ key: 'pdf', label: 'PDF', icon: '📄' },
|
||||
{ key: 'html', label: 'HTML', icon: '🌐' },
|
||||
{ key: 'markdown', label: 'Markdown', icon: '📝' },
|
||||
{ key: 'txt', label: '纯文本', icon: '📃' }
|
||||
]
|
||||
|
||||
// 清理Markdown内容的函数
|
||||
const cleanMarkdownContent = (content: string): string => {
|
||||
if (!content) return ''
|
||||
|
|
@ -281,6 +314,24 @@ const initVditor = async () => {
|
|||
if (toolbar) {
|
||||
console.log('- 工具栏可见性:', getComputedStyle(toolbar).display)
|
||||
console.log('- 工具栏宽度:', getComputedStyle(toolbar).width)
|
||||
|
||||
// 检查导出相关的按钮
|
||||
const exportButtons = toolbar.querySelectorAll('[data-type*="export"]')
|
||||
console.log('- 导出按钮数量:', exportButtons.length)
|
||||
exportButtons.forEach((btn, index) => {
|
||||
console.log(`- 导出按钮${index + 1}:`, btn.getAttribute('data-type'), btn.textContent)
|
||||
})
|
||||
|
||||
// 检查所有工具栏按钮
|
||||
const allButtons = toolbar.querySelectorAll('button, [data-type]')
|
||||
console.log('- 所有工具栏按钮:')
|
||||
allButtons.forEach((btn, index) => {
|
||||
const dataType = btn.getAttribute('data-type')
|
||||
const text = btn.textContent?.trim()
|
||||
if (dataType || text) {
|
||||
console.log(` ${index + 1}. data-type: ${dataType}, text: "${text}"`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}, 500)
|
||||
|
|
@ -326,9 +377,23 @@ const destroyVditor = () => {
|
|||
}
|
||||
|
||||
|
||||
// 点击外部关闭导出菜单
|
||||
const handleClickOutside = (event: Event) => {
|
||||
const target = event.target as HTMLElement
|
||||
if (!target.closest('.export-menu-container')) {
|
||||
showExportMenu.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时添加事件监听
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
onUnmounted(() => {
|
||||
destroyVditor()
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
const getTypeClass = (type: string) => {
|
||||
|
|
@ -353,18 +418,132 @@ const getTypeText = (type: string) => {
|
|||
|
||||
// 导航功能现在通过emit处理
|
||||
|
||||
const downloadReport = () => {
|
||||
// 切换导出菜单显示状态
|
||||
const toggleExportMenu = () => {
|
||||
showExportMenu.value = !showExportMenu.value
|
||||
}
|
||||
|
||||
// 检查并重新初始化Vditor
|
||||
const ensureVditorExists = () => {
|
||||
if (!vditorInstance.value && props.currentReport?.content) {
|
||||
console.log('Vditor实例不存在,重新初始化...')
|
||||
nextTick(() => {
|
||||
if (props.drawerOpen && props.currentReport) {
|
||||
setTimeout(() => {
|
||||
if (props.drawerOpen && props.currentReport) {
|
||||
initVditor()
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 导出报告(根据格式选择)
|
||||
const exportReport = (format: string) => {
|
||||
if (!props.currentReport) return
|
||||
|
||||
// 关闭菜单
|
||||
showExportMenu.value = false
|
||||
|
||||
console.log(`开始导出${format}格式的报告`)
|
||||
console.log('Vditor实例状态:', vditorInstance.value ? '存在' : '不存在')
|
||||
|
||||
// 确保Vditor实例存在
|
||||
ensureVditorExists()
|
||||
|
||||
// 如果Vditor实例存在,使用Vditor的导出功能
|
||||
if (vditorInstance.value && format !== 'txt') {
|
||||
try {
|
||||
const vditorElement = document.getElementById('vditor-report-content')
|
||||
if (vditorElement) {
|
||||
console.log('Vditor元素找到,开始查找导出按钮')
|
||||
|
||||
// 尝试多种可能的导出按钮选择器
|
||||
let exportButton: HTMLElement | null = null
|
||||
|
||||
// 方法1:查找Vditor工具栏中的导出相关按钮
|
||||
const toolbarButtons = vditorElement.querySelectorAll('.vditor-toolbar button, .vditor-toolbar [data-type]')
|
||||
console.log('找到的工具栏按钮数量:', toolbarButtons.length)
|
||||
|
||||
// 打印所有按钮的信息用于调试
|
||||
toolbarButtons.forEach((btn, index) => {
|
||||
const element = btn as HTMLElement
|
||||
console.log(`按钮${index}:`, {
|
||||
text: element.textContent?.trim(),
|
||||
dataType: element.getAttribute('data-type'),
|
||||
className: element.className,
|
||||
title: element.getAttribute('title')
|
||||
})
|
||||
})
|
||||
|
||||
// 方法2:根据格式查找特定按钮
|
||||
if (format === 'pdf') {
|
||||
exportButton = vditorElement.querySelector('[data-type="pdf"]') as HTMLElement
|
||||
} else if (format === 'html') {
|
||||
exportButton = vditorElement.querySelector('[data-type="html"]') as HTMLElement
|
||||
} else if (format === 'markdown') {
|
||||
exportButton = vditorElement.querySelector('[data-type="markdown"]') as HTMLElement
|
||||
}
|
||||
|
||||
console.log(`查找${format}导出按钮结果:`, exportButton)
|
||||
|
||||
// 如果找到对应的导出按钮,点击它
|
||||
if (exportButton) {
|
||||
console.log(`找到${format}导出按钮,准备点击`)
|
||||
exportButton.click()
|
||||
console.log(`已点击${format}导出按钮`)
|
||||
|
||||
// 导出完成后重新初始化Vditor以确保内容显示正常
|
||||
setTimeout(() => {
|
||||
if (!vditorInstance.value && props.currentReport?.content) {
|
||||
console.log('导出后重新初始化Vditor')
|
||||
initVditor()
|
||||
}
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
|
||||
// 方法3:尝试通用的导出按钮
|
||||
const generalExportButton = vditorElement.querySelector('[data-type="export"]') as HTMLElement ||
|
||||
vditorElement.querySelector('[title*="导出"]') as HTMLElement ||
|
||||
vditorElement.querySelector('[title*="export"]') as HTMLElement ||
|
||||
vditorElement.querySelector('button[title*="导出"]') as HTMLElement
|
||||
|
||||
if (generalExportButton) {
|
||||
console.log('找到通用导出按钮,准备点击')
|
||||
generalExportButton.click()
|
||||
console.log('已点击通用导出按钮')
|
||||
|
||||
// 导出完成后重新初始化Vditor以确保内容显示正常
|
||||
setTimeout(() => {
|
||||
if (!vditorInstance.value && props.currentReport?.content) {
|
||||
console.log('导出后重新初始化Vditor')
|
||||
initVditor()
|
||||
}
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
|
||||
console.log('未找到任何导出按钮')
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Vditor导出${format}失败:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
// 备用方案:直接下载文本文件
|
||||
console.log(`使用备用下载方案,格式:${format}`)
|
||||
const blob = new Blob([props.currentReport.content], { type: 'text/plain;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `${props.currentReport.title}.txt`
|
||||
a.download = `${props.currentReport.title}.${format === 'txt' ? 'txt' : format}`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
|
||||
const shareReport = () => {
|
||||
if (!props.currentReport) return
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue