优化报告导出功能和Vditor显示
- 实现自定义导出格式选择框,向上延展显示 - 修复Vditor导出功能,使用正确的按钮选择器 - 添加导出后Vditor内容自动恢复机制 - 优化用户体验,支持PDF、HTML、Markdown、纯文本导出 - 添加详细的调试日志和错误处理 - 修复导出后Vditor内容消失的问题
This commit is contained in:
parent
b635c1fd08
commit
e7ea6b98c8
|
|
@ -122,16 +122,40 @@
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-semibold text-gray-900 mb-3">操作</h3>
|
<h3 class="text-lg font-semibold text-gray-900 mb-3">操作</h3>
|
||||||
<div class="flex flex-wrap gap-3">
|
<div class="flex flex-wrap gap-3 relative">
|
||||||
<button
|
<!-- 下载按钮 -->
|
||||||
@click="downloadReport"
|
<div class="relative export-menu-container">
|
||||||
class="flex items-center gap-2 bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-colors"
|
<button
|
||||||
>
|
@click="toggleExportMenu"
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
class="flex items-center gap-2 bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-colors"
|
||||||
<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" 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" />
|
||||||
</button>
|
</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
|
<button
|
||||||
@click="shareReport"
|
@click="shareReport"
|
||||||
|
|
@ -163,7 +187,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onUnmounted, nextTick } from 'vue'
|
import { ref, watch, onUnmounted, onMounted, nextTick } from 'vue'
|
||||||
import Vditor from 'vditor'
|
import Vditor from 'vditor'
|
||||||
|
|
||||||
// Props定义
|
// Props定义
|
||||||
|
|
@ -182,6 +206,15 @@ const emit = defineEmits(['close', 'navigate', 'get-task-title'])
|
||||||
// Vditor实例
|
// Vditor实例
|
||||||
const vditorInstance = ref<Vditor | null>(null)
|
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内容的函数
|
// 清理Markdown内容的函数
|
||||||
const cleanMarkdownContent = (content: string): string => {
|
const cleanMarkdownContent = (content: string): string => {
|
||||||
if (!content) return ''
|
if (!content) return ''
|
||||||
|
|
@ -281,6 +314,24 @@ const initVditor = async () => {
|
||||||
if (toolbar) {
|
if (toolbar) {
|
||||||
console.log('- 工具栏可见性:', getComputedStyle(toolbar).display)
|
console.log('- 工具栏可见性:', getComputedStyle(toolbar).display)
|
||||||
console.log('- 工具栏宽度:', getComputedStyle(toolbar).width)
|
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)
|
}, 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(() => {
|
onUnmounted(() => {
|
||||||
destroyVditor()
|
destroyVditor()
|
||||||
|
document.removeEventListener('click', handleClickOutside)
|
||||||
})
|
})
|
||||||
|
|
||||||
const getTypeClass = (type: string) => {
|
const getTypeClass = (type: string) => {
|
||||||
|
|
@ -353,18 +418,132 @@ const getTypeText = (type: string) => {
|
||||||
|
|
||||||
// 导航功能现在通过emit处理
|
// 导航功能现在通过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
|
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 blob = new Blob([props.currentReport.content], { type: 'text/plain;charset=utf-8' })
|
||||||
const url = URL.createObjectURL(blob)
|
const url = URL.createObjectURL(blob)
|
||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = url
|
a.href = url
|
||||||
a.download = `${props.currentReport.title}.txt`
|
a.download = `${props.currentReport.title}.${format === 'txt' ? 'txt' : format}`
|
||||||
a.click()
|
a.click()
|
||||||
URL.revokeObjectURL(url)
|
URL.revokeObjectURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const shareReport = () => {
|
const shareReport = () => {
|
||||||
if (!props.currentReport) return
|
if (!props.currentReport) return
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue