From ef2e9c8d794e8acc07e207191712899d6e3eff7b Mon Sep 17 00:00:00 2001 From: yangzhe Date: Tue, 15 Jul 2025 09:21:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96Markdown=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=99=A8=EF=BC=8C=E4=BF=AE=E5=A4=8D=E7=B2=97=E4=BD=93?= =?UTF-8?q?=E5=92=8C=E5=88=97=E8=A1=A8=E9=A1=B9=E6=98=BE=E7=A4=BA=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=86=85=E5=AE=B9=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E6=8D=A2=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../markdown-viewer/markdown-viewer.vue | 499 +++++++++++++++++- 1 file changed, 481 insertions(+), 18 deletions(-) diff --git a/components/markdown-viewer/markdown-viewer.vue b/components/markdown-viewer/markdown-viewer.vue index a7dce03..1516b82 100644 --- a/components/markdown-viewer/markdown-viewer.vue +++ b/components/markdown-viewer/markdown-viewer.vue @@ -9,9 +9,41 @@ import { marked } from 'marked'; import hljs from 'highlight.js'; import mpHtml from '@/node_modules/mp-html/dist/uni-app/components/mp-html/mp-html.vue'; +// 自定义渲染器,修改列表的渲染方式 +const renderer = new marked.Renderer(); + +// 重写粗体渲染方法,确保粗体文本不会导致不当换行 +renderer.strong = function(text) { + // 确保文本周围没有不必要的空格 + const trimmedText = text.trim(); + return `${trimmedText}`; +}; + +// 重写列表项渲染方法 - 使用特殊的包装来确保内容在一行 +renderer.listitem = function(text) { + return `
  • ${text}
  • `; +}; + +// 重写有序列表渲染方法 +renderer.list = function(body, ordered) { + const type = ordered ? 'ol' : 'ul'; + const className = ordered ? 'custom-ordered-list' : 'custom-unordered-list'; + return `<${type} class="${className}">${body}`; +}; + +// 重写段落渲染方法,确保换行正确 +renderer.paragraph = function(text) { + return `

    ${text}

    `; +}; + +// 重写链接渲染方法,确保链接文本正常显示 +renderer.link = function(href, title, text) { + return `${text}`; +}; + // 配置marked marked.use({ - renderer: new marked.Renderer(), + renderer: renderer, highlight: function(code, lang) { try { return hljs.highlightAuto(code, [lang]).value; @@ -54,33 +86,294 @@ export default { renderedContent() { if (!this.content) return ''; try { - // 添加自定义样式,确保内容自动换行 + // 添加自定义样式,确保内容自动换行和列表正确显示 const styleTag = ``; - // 渲染Markdown为HTML - const htmlContent = marked(this.content); + // 对Markdown内容进行预处理,修复可能导致问题的模式 + let processedContent = this.content; + + // 将Markdown内容分解为段落,分别处理每个段落 + const paragraphs = processedContent.split('\n\n'); + const processedParagraphs = paragraphs.map(paragraph => { + // 处理一个段落内的粗体文本 + return paragraph.replace(/\*\*([^*]+?)\*\*/g, (match, content) => { + // 返回无缝拼接的HTML粗体标签 + return `${content}`; + }); + }); + + // 重新组合处理后的段落 + processedContent = processedParagraphs.join('\n\n'); + + // 使用marked处理剩余的Markdown语法 + let htmlContent = marked(processedContent); + + // 后处理HTML,确保任何遗漏的粗体标记也能被处理 + htmlContent = htmlContent + // 处理任何遗漏的**标记 + .replace(/\*\*([^*]+?)\*\*/g, (match, content) => { + return `${content}`; + }); + + // 处理HTML,确保所有文本元素都有正确的显示属性 + let processedHtml = htmlContent; + + // 将所有段落包装在特殊div中,防止段落间的换行问题 + processedHtml = processedHtml.replace(/]*)>(.*?)<\/p>/gs, (match, attrs, content) => { + // 确保段落内容连续显示,不被换行打断 + return `${content}

    `; + }); + + // 确保强制使用行内样式 + processedHtml = processedHtml.replace( + /'); + + // 添加强制粗体样式覆盖 + processedHtml = ` + + ` + processedHtml; // 返回带样式的HTML - return styleTag + htmlContent; + return styleTag + processedHtml; } catch (error) { console.error('Markdown渲染错误:', error); return `

    Markdown渲染错误: ${error.message}

    `; @@ -97,28 +390,198 @@ export default { padding: 20rpx; overflow-wrap: break-word; word-wrap: break-word; - word-break: normal; /* 改为normal,避免中文被强制断行 */ + word-break: normal; } -/* 全局样式,确保所有元素都能自动换行 */ +/* 全局样式 */ .markdown-container >>> * { max-width: 100% !important; white-space: normal !important; - word-break: normal !important; /* 改为normal */ + word-break: normal !important; overflow-wrap: break-word !important; + box-sizing: border-box !important; } -/* 特别处理文本段落,避免单字换行 */ +/* 段落样式 */ +.markdown-container >>> .custom-paragraph, .markdown-container >>> p { - display: inline-block !important; - text-align: left !important; + display: block !important; + /* margin: 10rpx 0 !important; */ + width: 100% !important; + line-height: 1.6 !important; + text-align: left !important; /* 改为左对齐,不要两端对齐 */ } -/* 特别处理代码块 */ +/* 专门用于列表项内容包装的容器 */ +.markdown-container >>> .list-item-wrapper { + display: inline-block !important; + width: 100% !important; + white-space: normal !important; + /* 关键属性:防止内部元素分列 */ + table-layout: fixed !important; + word-break: normal !important; + text-align: left !important; + text-justify: none !important; + letter-spacing: normal !important; + word-spacing: normal !important; + text-align-last: left !important; +} + +/* 内联元素修复 */ +.markdown-container >>> span, +.markdown-container >>> a, +.markdown-container >>> em, +.markdown-container >>> strong, +.markdown-container >>> .custom-strong, +.markdown-container >>> b { + display: inline !important; + white-space: normal !important; + vertical-align: baseline !important; + float: none !important; + position: static !important; + width: auto !important; + height: auto !important; + /* 确保内部不会有奇怪的换行 */ + word-break: keep-all !important; + line-height: inherit !important; +} + +/* 特别针对粗体文本 */ +.markdown-container >>> .custom-strong, +.markdown-container >>> strong, +.markdown-container >>> b { + font-weight: 900 !important; + display: inline !important; + vertical-align: baseline !important; + position: static !important; + margin: 0 !important; + padding: 0 !important; + border: none !important; + float: none !important; + width: auto !important; + white-space: normal !important; + word-break: normal !important; + overflow-wrap: break-word !important; + word-wrap: normal !important; + line-height: inherit !important; + color: inherit !important; /* 确保颜色正确继承 */ + font-size: inherit !important; /* 确保字体大小正确继承 */ + font-family: inherit !important; /* 确保字体系列正确继承 */ + text-decoration: none !important; +} + +/* 有序列表样式 */ +.markdown-container >>> .custom-ordered-list, +.markdown-container >>> ol { + display: block !important; + counter-reset: item !important; + padding-left: 0 !important; + margin: 10rpx 0 !important; + width: 100% !important; + list-style-type: none !important; +} + +/* 无序列表样式 */ +.markdown-container >>> .custom-unordered-list, +.markdown-container >>> ul { + display: block !important; + padding-left: 0 !important; + margin: 10rpx 0 !important; + width: 100% !important; + list-style-type: none !important; +} + +/* 列表项共用样式 */ +.markdown-container >>> .custom-list-item, +.markdown-container >>> ol li, +.markdown-container >>> ul li { + display: flex !important; + padding-left: 0 !important; + margin-bottom: 8rpx !important; + width: 100% !important; + position: relative !important; + /* 确保内容在一行显示 */ + flex-wrap: nowrap !important; + align-items: flex-start !important; /* 顶部对齐 */ +} + +/* 列表项内容样式 */ +.markdown-container >>> .custom-list-item > *:not(ol):not(ul), +.markdown-container >>> ol li > *:not(ol):not(ul), +.markdown-container >>> ul li > *:not(ol):not(ul) { + flex: 1 !important; + display: inline !important; + white-space: normal !important; + word-break: normal !important; + width: auto !important; + /* 确保文本不会被分列 */ + column-count: 1 !important; + column-width: auto !important; + column-span: all !important; + text-align: left !important; /* 确保文本左对齐 */ + justify-content: flex-start !important; /* 左对齐 */ + text-justify: none !important; /* 禁用两端对齐 */ + letter-spacing: normal !important; /* 正常字间距 */ + word-spacing: normal !important; /* 正常词间距 */ +} + +/* 列表项内的p标签特殊处理 */ +.markdown-container >>> ol li p, +.markdown-container >>> ul li p, +.markdown-container >>> .custom-list-item p { + display: inline !important; + margin: 0 !important; + padding: 0 !important; + width: auto !important; + white-space: normal !important; +} + +/* 有序列表项编号 */ +.markdown-container >>> .custom-ordered-list > .custom-list-item::before, +.markdown-container >>> ol > li::before { + counter-increment: item !important; + content: counter(item)"." !important; + min-width: 40rpx !important; + width: 40rpx !important; + font-weight: bold !important; + text-align: right !important; + margin-right: 8rpx !important; + padding-right: 0 !important; + flex-shrink: 0 !important; + align-self: flex-start !important; /* 顶部对齐 */ + /* padding-top: 1rpx !important; 微调垂直位置 */ +} + +/* 无序列表项标记 */ +.markdown-container >>> .custom-unordered-list > .custom-list-item::before, +.markdown-container >>> ul > li::before { + content: "-" !important; + min-width: 40rpx !important; + width: 40rpx !important; + font-weight: bold !important; + text-align: center !important; + margin-right: 8rpx !important; + padding-right: 0 !important; + flex-shrink: 0 !important; + align-self: flex-start !important; /* 顶部对齐 */ + /* padding-top: 1rpx !important; 微调垂直位置 */ +} + +/* 禁用可能导致问题的CSS特性 */ +.markdown-container >>> * { + column-count: 1 !important; + column-gap: 0 !important; + column-rule: none !important; + column-span: all !important; + column-width: auto !important; + columns: auto !important; +} + +/* 代码块样式 */ .markdown-container >>> pre, .markdown-container >>> code { white-space: pre-wrap !important; - word-break: break-all !important; /* 代码可以在任何字符间断行 */ + word-break: break-all !important; overflow-wrap: break-word !important; max-width: 100% !important; }