修复Vditor工具栏显示异常问题
- 在main.ts中添加Vditor CSS样式引入 - 修复ReportDrawer.vue中Vditor配置 - 使用官方文档推荐的完整工具栏配置 - 添加nextTick确保DOM完全渲染 - 修复工具栏pin配置和版本号匹配
This commit is contained in:
parent
1b75c934b3
commit
b635c1fd08
|
|
@ -77,11 +77,11 @@
|
||||||
|
|
||||||
<!-- 报告内容 -->
|
<!-- 报告内容 -->
|
||||||
<div class="prose max-w-none">
|
<div class="prose max-w-none">
|
||||||
<!-- 有内容时显示HTML渲染的内容 -->
|
<!-- 有内容时显示Vditor渲染的Markdown -->
|
||||||
<div
|
<div
|
||||||
v-if="props.currentReport?.content && props.currentReport.content.trim()"
|
v-if="props.currentReport?.content && props.currentReport.content.trim()"
|
||||||
|
id="vditor-report-content"
|
||||||
class="text-gray-700 leading-relaxed"
|
class="text-gray-700 leading-relaxed"
|
||||||
v-html="getRenderedContent()"
|
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<!-- 无内容时显示提示信息 -->
|
<!-- 无内容时显示提示信息 -->
|
||||||
|
|
@ -163,7 +163,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 移除Vditor后不再需要Vue的响应式导入
|
import { ref, watch, onUnmounted, nextTick } from 'vue'
|
||||||
|
import Vditor from 'vditor'
|
||||||
|
|
||||||
// Props定义
|
// Props定义
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
@ -178,6 +179,9 @@ const props = defineProps({
|
||||||
// Emits定义
|
// Emits定义
|
||||||
const emit = defineEmits(['close', 'navigate', 'get-task-title'])
|
const emit = defineEmits(['close', 'navigate', 'get-task-title'])
|
||||||
|
|
||||||
|
// Vditor实例
|
||||||
|
const vditorInstance = ref<Vditor | null>(null)
|
||||||
|
|
||||||
// 清理Markdown内容的函数
|
// 清理Markdown内容的函数
|
||||||
const cleanMarkdownContent = (content: string): string => {
|
const cleanMarkdownContent = (content: string): string => {
|
||||||
if (!content) return ''
|
if (!content) return ''
|
||||||
|
|
@ -192,35 +196,6 @@ const cleanMarkdownContent = (content: string): string => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 简单的Markdown转HTML函数
|
|
||||||
const markdownToHtml = (markdown: string): string => {
|
|
||||||
if (!markdown) return ''
|
|
||||||
|
|
||||||
let html = markdown
|
|
||||||
|
|
||||||
// 标题
|
|
||||||
html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
|
||||||
html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
|
||||||
html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
|
||||||
|
|
||||||
// 粗体和斜体
|
|
||||||
html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
||||||
html = html.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
||||||
|
|
||||||
// 列表
|
|
||||||
html = html.replace(/^- (.*$)/gim, '<li>$1</li>')
|
|
||||||
html = html.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
|
|
||||||
|
|
||||||
// 段落
|
|
||||||
html = html.replace(/\n\n/g, '</p><p>')
|
|
||||||
html = '<p>' + html + '</p>'
|
|
||||||
|
|
||||||
// 清理空段落
|
|
||||||
html = html.replace(/<p><\/p>/g, '')
|
|
||||||
html = html.replace(/<p>\s*<\/p>/g, '')
|
|
||||||
|
|
||||||
return html
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取清理后的报告内容
|
// 获取清理后的报告内容
|
||||||
const getReportContent = () => {
|
const getReportContent = () => {
|
||||||
|
|
@ -228,13 +203,133 @@ const getReportContent = () => {
|
||||||
return cleanMarkdownContent(props.currentReport.content)
|
return cleanMarkdownContent(props.currentReport.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取渲染后的HTML内容
|
// 初始化Vditor(纯预览模式)
|
||||||
const getRenderedContent = () => {
|
const initVditor = async () => {
|
||||||
const content = getReportContent()
|
// 检查组件是否还存在
|
||||||
return markdownToHtml(content)
|
if (!props.drawerOpen || !props.currentReport) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安全销毁现有实例
|
||||||
|
destroyVditor()
|
||||||
|
|
||||||
|
// 确保DOM元素存在
|
||||||
|
const element = document.getElementById('vditor-report-content')
|
||||||
|
if (!element) {
|
||||||
|
console.error('Vditor容器元素不存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空容器内容
|
||||||
|
element.innerHTML = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
vditorInstance.value = new Vditor('vditor-report-content', {
|
||||||
|
height: 500,
|
||||||
|
toolbar: [
|
||||||
|
'emoji', 'headings', 'bold', 'italic', 'strike', 'link', '|',
|
||||||
|
'list', 'ordered-list', 'check', 'outdent', 'indent', '|',
|
||||||
|
'quote', 'line', 'code', 'inline-code', 'insert-before', 'insert-after', '|',
|
||||||
|
'table', '|', 'undo', 'redo', '|', 'fullscreen', 'edit-mode', '|',
|
||||||
|
'content-theme', 'code-theme', 'export', 'outline', 'preview', 'devtools', 'info', 'help'
|
||||||
|
],
|
||||||
|
toolbarConfig: {
|
||||||
|
pin: true,
|
||||||
|
},
|
||||||
|
cache: {
|
||||||
|
enable: false,
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
theme: {
|
||||||
|
current: 'light',
|
||||||
|
path: 'https://unpkg.com/vditor@3.11.2/dist/css/content-theme'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mode: 'wysiwyg',
|
||||||
|
after: () => {
|
||||||
|
// 再次检查组件状态
|
||||||
|
if (!props.drawerOpen || !props.currentReport) {
|
||||||
|
destroyVditor()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Vditor初始化完成,工具栏已显示')
|
||||||
|
|
||||||
|
// 设置内容
|
||||||
|
if (vditorInstance.value) {
|
||||||
|
try {
|
||||||
|
vditorInstance.value.setValue(getReportContent())
|
||||||
|
console.log('Vditor内容设置完成')
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Vditor设置内容失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查工具栏是否正确渲染
|
||||||
|
setTimeout(() => {
|
||||||
|
const vditorElement = document.getElementById('vditor-report-content')
|
||||||
|
if (vditorElement) {
|
||||||
|
const toolbar = vditorElement.querySelector('.vditor-toolbar')
|
||||||
|
const editor = vditorElement.querySelector('.vditor-wysiwyg')
|
||||||
|
const preview = vditorElement.querySelector('.vditor-preview')
|
||||||
|
|
||||||
|
console.log('Vditor元素检查:')
|
||||||
|
console.log('- 工具栏存在:', !!toolbar)
|
||||||
|
console.log('- 编辑器存在:', !!editor)
|
||||||
|
console.log('- 预览存在:', !!preview)
|
||||||
|
|
||||||
|
if (toolbar) {
|
||||||
|
console.log('- 工具栏可见性:', getComputedStyle(toolbar).display)
|
||||||
|
console.log('- 工具栏宽度:', getComputedStyle(toolbar).width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Vditor初始化失败:', error)
|
||||||
|
vditorInstance.value = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导航和状态信息现在通过props传递
|
// 监听报告变化和抽屉状态
|
||||||
|
watch([() => props.currentReport, () => props.drawerOpen], ([newReport, isOpen]) => {
|
||||||
|
if (isOpen && newReport && newReport.content) {
|
||||||
|
// 使用nextTick确保DOM完全渲染
|
||||||
|
nextTick(() => {
|
||||||
|
if (props.drawerOpen && props.currentReport) {
|
||||||
|
// 再次延迟确保抽屉完全展开
|
||||||
|
setTimeout(() => {
|
||||||
|
if (props.drawerOpen && props.currentReport) {
|
||||||
|
initVditor()
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (!isOpen) {
|
||||||
|
// 关闭抽屉时安全销毁Vditor实例
|
||||||
|
destroyVditor()
|
||||||
|
}
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
|
// 安全销毁Vditor的函数
|
||||||
|
const destroyVditor = () => {
|
||||||
|
if (vditorInstance.value) {
|
||||||
|
try {
|
||||||
|
vditorInstance.value.destroy()
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Vditor销毁时出错:', error)
|
||||||
|
} finally {
|
||||||
|
vditorInstance.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 组件卸载时清理
|
||||||
|
onUnmounted(() => {
|
||||||
|
destroyVditor()
|
||||||
|
})
|
||||||
|
|
||||||
const getTypeClass = (type: string) => {
|
const getTypeClass = (type: string) => {
|
||||||
const typeClasses = {
|
const typeClasses = {
|
||||||
|
|
@ -304,7 +399,23 @@ const copyToClipboard = async () => {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h1 {
|
/* Vditor样式 */
|
||||||
|
#vditor-report-content {
|
||||||
|
border: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vditor-report-content :deep(.vditor-preview) {
|
||||||
|
border: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
color: #374151 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vditor-report-content :deep(.vditor-preview h1) {
|
||||||
font-size: 1.875rem;
|
font-size: 1.875rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
|
|
@ -312,7 +423,7 @@ const copyToClipboard = async () => {
|
||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h2 {
|
#vditor-report-content :deep(.vditor-preview h2) {
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
|
|
@ -320,7 +431,7 @@ const copyToClipboard = async () => {
|
||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose h3 {
|
#vditor-report-content :deep(.vditor-preview h3) {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-top: 1.25rem;
|
margin-top: 1.25rem;
|
||||||
|
|
@ -328,29 +439,29 @@ const copyToClipboard = async () => {
|
||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose p {
|
#vditor-report-content :deep(.vditor-preview p) {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
color: #374151;
|
color: #374151;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose ul {
|
#vditor-report-content :deep(.vditor-preview ul) {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
padding-left: 1.5rem;
|
padding-left: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose li {
|
#vditor-report-content :deep(.vditor-preview li) {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #374151;
|
color: #374151;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose strong {
|
#vditor-report-content :deep(.vditor-preview strong) {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
.prose em {
|
#vditor-report-content :deep(.vditor-preview em) {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
color: #4b5563;
|
color: #4b5563;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import App from './App.vue'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus'
|
||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css'
|
||||||
|
import 'vditor/dist/index.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue